Retire snd_sbc ISA sound card driver

Along with the snd_sb8 and snd_sb16 drivers.  They supported ISA
Creative Sound Blaster and compatible sound cards.

Note that isa/sb.h is not removed, as it is still used by some PCI
sound card drivers.

ISA sound card drivers are deprecated as discussed on the current[1] and
stable[2] mailing lists.  Deprecation notices were added in e39ec8933b
and MFCd to stable branches.

Driver removals are being committed individually so that specific
drivers can be restored if necessary (either in FreeBSD or by downstream
projects).

[1] https://lists.freebsd.org/archives/freebsd-current/2022-March/001680.html
[2] https://lists.freebsd.org/archives/freebsd-stable/2022-March/000585.html

Reviewed by:	mav
Relnotes:	Yes
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D34671
This commit is contained in:
Ed Maste 2022-06-21 14:50:04 -04:00
parent 5126e5eeeb
commit 716924cb48
13 changed files with 4 additions and 2648 deletions

View File

@ -58,6 +58,9 @@ OLD_FILES+=usr/share/man/man4/snd_ad1816.4
OLD_FILES+=usr/share/man/man4/snd_ess.4
OLD_FILES+=usr/share/man/man4/snd_gusc.4
OLD_FILES+=usr/share/man/man4/snd_mss.4
OLD_FILES+=usr/share/man/man4/snd_sb16.4
OLD_FILES+=usr/share/man/man4/snd_sb8.4
OLD_FILES+=usr/share/man/man4/snd_sbc.4
# 20220612: new clang import which bumps version from 14.0.4 to 14.0.5
OLD_FILES+=usr/lib/clang/14.0.4/include/cuda_wrappers/algorithm

View File

@ -517,7 +517,6 @@ MAN= aac.4 \
snd_ich.4 \
snd_maestro3.4 \
snd_neomagic.4 \
snd_sbc.4 \
snd_solo.4 \
snd_spicds.4 \
snd_t4dwave.4 \
@ -737,8 +736,6 @@ MLINKS+=sk.4 if_sk.4
MLINKS+=smp.4 SMP.4
MLINKS+=smsc.4 if_smsc.4
MLINKS+=snd_envy24.4 snd_ak452x.4
MLINKS+=snd_sbc.4 snd_sb16.4 \
snd_sbc.4 snd_sb8.4
MLINKS+=${_spkr.4} ${_speaker.4}
MLINKS+=splash.4 screensaver.4
MLINKS+=ste.4 if_ste.4

View File

@ -111,12 +111,6 @@ The following bridge device drivers are available:
.It
.Xr snd_neomagic 4
.It
snd_sb16
.It
snd_sb8
.It
.Xr snd_sbc 4
.It
.Xr snd_solo 4
.It
.Xr snd_spicds 4
@ -708,7 +702,6 @@ A device node is not created properly.
.Xr snd_ich 4 ,
.Xr snd_maestro3 4 ,
.Xr snd_neomagic 4 ,
.Xr snd_sbc 4 ,
.Xr snd_solo 4 ,
.Xr snd_spicds 4 ,
.Xr snd_t4dwave 4 ,

View File

@ -1,135 +0,0 @@
.\"
.\" Copyright (c) 1999 Seigo Tanimura
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd March 19, 2022
.Dt SND_SBC 4
.Os
.Sh NAME
.Nm snd_sbc ,
.Nm snd_sb16 ,
.Nm snd_sb8
.Nd Creative Sound Blaster ISA and compatible bridge device driver
.Sh DEPRECATION NOTICE
This driver is scheduled for removal prior to the release of
.Fx 14.0 .
.Sh SYNOPSIS
To compile this driver into the kernel, place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device sound"
.Cd "device snd_sbc"
.Cd "device snd_sb16"
.Cd "device snd_sb8"
.Ed
.Pp
Alternatively, to load the driver as a module at boot time, place the
following lines in
.Xr loader.conf 5 :
.Bd -literal -offset indent
snd_sbc_load="YES"
snd_sb16_load="YES"
snd_sb8_load="YES"
.Ed
.Pp
Non-PnP cards require the following lines in
.Xr device.hints 5 :
.Bd -literal -offset indent
hint.sbc.0.at="isa"
hint.sbc.0.port="0x220"
hint.sbc.0.irq="5"
hint.sbc.0.drq="1"
hint.sbc.0.flags="0x15"
.Ed
.Sh DESCRIPTION
The
.Nm
bridge driver allows the generic audio driver
.Xr sound 4
to attach to Creative Sound Blaster ISA (mostly SB16 or SB8, known as
SoundBlaster Pro) compatible audio cards.
.Pp
The value of flags specifies the secondary DMA channel.
If the secondary
DMA channel is C, set the flags to (C | 0x10).
For a sound card without the
secondary DMA channel, the flags should be set to zero.
.Sh HARDWARE
The
.Nm
driver supports the following sound cards:
.Pp
.Bl -bullet -compact
.It
Avance Asound 110
.It
Avance Logic ALS100+
.It
Avance Logic ALS120
.It
Creative SB16
.It
Creative SB32
.It
Creative AWE64
.It
Creative AWE64 Gold
.It
Creative ViBRA16C
.It
Creative ViBRA16X
.It
ESS ES1681
.It
ESS ES1688
.It
ESS ES1868
.It
ESS ES1869
.It
ESS ES1878
.It
ESS ES1879
.It
ESS ES1888
.El
.Sh DIAGNOSTICS
.Bl -diag
.It sb_dspwr(XX) timed out.
A command to the DSP has timed out.
Check the I/O port configuration.
.It bad irq XX (5/7/9/10 valid)
The IRQ given to the driver is not valid.
.El
.Sh SEE ALSO
.Xr sound 4
.Sh HISTORY
The
.Nm
device driver first appeared in
.Fx 4.0 .
.Sh AUTHORS
.An Seigo Tanimura Aq Mt tanimura@r.dl.itc.u-tokyo.ac.jp

View File

@ -3089,9 +3089,6 @@ dev/smc/if_smc_fdt.c optional smc fdt
dev/snp/snp.c optional snp
dev/sound/clone.c optional sound
dev/sound/unit.c optional sound
dev/sound/isa/sb16.c optional snd_sb16 isa
dev/sound/isa/sb8.c optional snd_sb8 isa
dev/sound/isa/sbc.c optional snd_sbc isa
dev/sound/isa/sndbuf_dma.c optional sound isa
dev/sound/pci/als4000.c optional snd_als4000 pci
dev/sound/pci/atiixp.c optional snd_atiixp pci

View File

@ -73,9 +73,6 @@ MODULE_DEPEND(snd_driver, snd_hda, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_ich, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_maestro3, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_neomagic, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_sb16, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_sb8, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_sbc, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_solo, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_spicds, 1, 1, 1);
MODULE_DEPEND(snd_driver, snd_t4dwave, 1, 1, 1);

View File

@ -1,912 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
* Copyright (c) 1997,1998 Luigi Rizzo
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
* conditions.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/pcm/sound.h>
#include <dev/sound/isa/sb.h>
#include <dev/sound/chip.h>
#include <isa/isavar.h>
#include "mixer_if.h"
SND_DECLARE_FILE("$FreeBSD$");
#define SB16_BUFFSIZE 4096
#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16)
static u_int32_t sb16_fmt8[] = {
SND_FORMAT(AFMT_U8, 1, 0),
SND_FORMAT(AFMT_U8, 2, 0),
0
};
static struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0};
static u_int32_t sb16_fmt16[] = {
SND_FORMAT(AFMT_S16_LE, 1, 0),
SND_FORMAT(AFMT_S16_LE, 2, 0),
0
};
static struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0};
static u_int32_t sb16x_fmt[] = {
SND_FORMAT(AFMT_U8, 1, 0),
SND_FORMAT(AFMT_U8, 2, 0),
SND_FORMAT(AFMT_S16_LE, 1, 0),
SND_FORMAT(AFMT_S16_LE, 2, 0),
0
};
static struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0};
struct sb_info;
struct sb_chinfo {
struct sb_info *parent;
struct pcm_channel *channel;
struct snd_dbuf *buffer;
int dir, run, dch;
u_int32_t fmt, spd, blksz;
};
struct sb_info {
struct resource *io_base; /* I/O address for the board */
struct resource *irq;
struct resource *drq1;
struct resource *drq2;
void *ih;
bus_dma_tag_t parent_dmat;
unsigned int bufsize;
int bd_id;
u_long bd_flags; /* board-specific flags */
int prio, prio16;
struct sb_chinfo pch, rch;
device_t parent_dev;
};
#if 0
static void sb_lock(struct sb_info *sb);
static void sb_unlock(struct sb_info *sb);
static int sb_rd(struct sb_info *sb, int reg);
static void sb_wr(struct sb_info *sb, int reg, u_int8_t val);
static int sb_cmd(struct sb_info *sb, u_char val);
/* static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); */
static int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
static u_int sb_get_byte(struct sb_info *sb);
static void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
static int sb_getmixer(struct sb_info *sb, u_int port);
static int sb_reset_dsp(struct sb_info *sb);
static void sb_intr(void *arg);
#endif
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd1 write a CMD + 1 byte arg
* sb_cmd2 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*/
static void
sb_lock(struct sb_info *sb) {
sbc_lock(device_get_softc(sb->parent_dev));
}
static void
sb_lockassert(struct sb_info *sb) {
sbc_lockassert(device_get_softc(sb->parent_dev));
}
static void
sb_unlock(struct sb_info *sb) {
sbc_unlock(device_get_softc(sb->parent_dev));
}
static int
port_rd(struct resource *port, int off)
{
return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off);
}
static void
port_wr(struct resource *port, int off, u_int8_t data)
{
bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data);
}
static int
sb_rd(struct sb_info *sb, int reg)
{
return port_rd(sb->io_base, reg);
}
static void
sb_wr(struct sb_info *sb, int reg, u_int8_t val)
{
port_wr(sb->io_base, reg, val);
}
static int
sb_dspwr(struct sb_info *sb, u_char val)
{
int i;
for (i = 0; i < 1000; i++) {
if ((sb_rd(sb, SBDSP_STATUS) & 0x80))
DELAY((i > 100)? 1000 : 10);
else {
sb_wr(sb, SBDSP_CMD, val);
return 1;
}
}
if (curthread->td_intr_nesting_level == 0)
printf("sb_dspwr(0x%02x) timed out.\n", val);
return 0;
}
static int
sb_cmd(struct sb_info *sb, u_char val)
{
#if 0
printf("sb_cmd: %x\n", val);
#endif
return sb_dspwr(sb, val);
}
/*
static int
sb_cmd1(struct sb_info *sb, u_char cmd, int val)
{
#if 0
printf("sb_cmd1: %x, %x\n", cmd, val);
#endif
if (sb_dspwr(sb, cmd)) {
return sb_dspwr(sb, val & 0xff);
} else return 0;
}
*/
static int
sb_cmd2(struct sb_info *sb, u_char cmd, int val)
{
int r;
#if 0
printf("sb_cmd2: %x, %x\n", cmd, val);
#endif
sb_lockassert(sb);
r = 0;
if (sb_dspwr(sb, cmd)) {
if (sb_dspwr(sb, val & 0xff)) {
if (sb_dspwr(sb, (val >> 8) & 0xff)) {
r = 1;
}
}
}
return r;
}
/*
* in the SB, there is a set of indirect "mixer" registers with
* address at offset 4, data at offset 5
*/
static void
sb_setmixer(struct sb_info *sb, u_int port, u_int value)
{
sb_lock(sb);
sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff));
DELAY(10);
sb_unlock(sb);
}
static int
sb_getmixer(struct sb_info *sb, u_int port)
{
int val;
sb_lockassert(sb);
sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = sb_rd(sb, SB_MIX_DATA);
DELAY(10);
return val;
}
static u_int
sb_get_byte(struct sb_info *sb)
{
int i;
for (i = 1000; i > 0; i--) {
if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80)
return sb_rd(sb, DSP_READ);
else
DELAY(20);
}
return 0xffff;
}
static int
sb_reset_dsp(struct sb_info *sb)
{
u_char b;
sb_lockassert(sb);
sb_wr(sb, SBDSP_RST, 3);
DELAY(100);
sb_wr(sb, SBDSP_RST, 0);
b = sb_get_byte(sb);
if (b != 0xAA) {
DEB(printf("sb_reset_dsp 0x%lx failed\n",
rman_get_start(sb->io_base)));
return ENXIO; /* Sorry */
}
return 0;
}
/************************************************************/
struct sb16_mixent {
int reg;
int bits;
int ofs;
int stereo;
};
static const struct sb16_mixent sb16_mixtab[32] = {
[SOUND_MIXER_VOLUME] = { 0x30, 5, 3, 1 },
[SOUND_MIXER_PCM] = { 0x32, 5, 3, 1 },
[SOUND_MIXER_SYNTH] = { 0x34, 5, 3, 1 },
[SOUND_MIXER_CD] = { 0x36, 5, 3, 1 },
[SOUND_MIXER_LINE] = { 0x38, 5, 3, 1 },
[SOUND_MIXER_MIC] = { 0x3a, 5, 3, 0 },
[SOUND_MIXER_SPEAKER] = { 0x3b, 5, 3, 0 },
[SOUND_MIXER_IGAIN] = { 0x3f, 2, 6, 1 },
[SOUND_MIXER_OGAIN] = { 0x41, 2, 6, 1 },
[SOUND_MIXER_TREBLE] = { 0x44, 4, 4, 1 },
[SOUND_MIXER_BASS] = { 0x46, 4, 4, 1 },
[SOUND_MIXER_LINE1] = { 0x52, 5, 3, 1 }
};
static int
sb16mix_init(struct snd_mixer *m)
{
struct sb_info *sb = mix_getdevinfo(m);
mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER |
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD |
SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE);
mix_setrecdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_LINE |
SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_CD);
sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */
sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */
sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */
return 0;
}
static int
rel2abs_volume(int x, int max)
{
int temp;
temp = ((x * max) + 50) / 100;
if (temp > max)
temp = max;
else if (temp < 0)
temp = 0;
return (temp);
}
static int
sb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct sb_info *sb = mix_getdevinfo(m);
const struct sb16_mixent *e;
int max;
e = &sb16_mixtab[dev];
max = (1 << e->bits) - 1;
left = rel2abs_volume(left, max);
right = rel2abs_volume(right, max);
sb_setmixer(sb, e->reg, left << e->ofs);
if (e->stereo)
sb_setmixer(sb, e->reg + 1, right << e->ofs);
else
right = left;
left = (left * 100) / max;
right = (right * 100) / max;
return left | (right << 8);
}
static u_int32_t
sb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
{
struct sb_info *sb = mix_getdevinfo(m);
u_char recdev_l, recdev_r;
recdev_l = 0;
recdev_r = 0;
if (src & SOUND_MASK_MIC) {
recdev_l |= 0x01; /* mono mic */
recdev_r |= 0x01;
}
if (src & SOUND_MASK_CD) {
recdev_l |= 0x04; /* l cd */
recdev_r |= 0x02; /* r cd */
}
if (src & SOUND_MASK_LINE) {
recdev_l |= 0x10; /* l line */
recdev_r |= 0x08; /* r line */
}
if (src & SOUND_MASK_SYNTH) {
recdev_l |= 0x40; /* l midi */
recdev_r |= 0x20; /* r midi */
}
sb_setmixer(sb, SB16_IMASK_L, recdev_l);
sb_setmixer(sb, SB16_IMASK_R, recdev_r);
/* Switch on/off FM tuner source */
if (src & SOUND_MASK_LINE1)
sb_setmixer(sb, 0x4a, 0x0c);
else
sb_setmixer(sb, 0x4a, 0x00);
/*
* since the same volume controls apply to the input and
* output sections, the best approach to have a consistent
* behaviour among cards would be to disable the output path
* on devices which are used to record.
* However, since users like to have feedback, we only disable
* the mic -- permanently.
*/
sb_setmixer(sb, SB16_OMASK, 0x1f & ~1);
return src;
}
static kobj_method_t sb16mix_mixer_methods[] = {
KOBJMETHOD(mixer_init, sb16mix_init),
KOBJMETHOD(mixer_set, sb16mix_set),
KOBJMETHOD(mixer_setrecsrc, sb16mix_setrecsrc),
KOBJMETHOD_END
};
MIXER_DECLARE(sb16mix_mixer);
/************************************************************/
static void
sb16_release_resources(struct sb_info *sb, device_t dev)
{
if (sb->irq) {
if (sb->ih)
bus_teardown_intr(dev, sb->irq, sb->ih);
bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
sb->irq = NULL;
}
if (sb->drq2) {
if (sb->drq2 != sb->drq1) {
isa_dma_release(rman_get_start(sb->drq2));
bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2);
}
sb->drq2 = NULL;
}
if (sb->drq1) {
isa_dma_release(rman_get_start(sb->drq1));
bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1);
sb->drq1 = NULL;
}
if (sb->io_base) {
bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
sb->io_base = NULL;
}
if (sb->parent_dmat) {
bus_dma_tag_destroy(sb->parent_dmat);
sb->parent_dmat = 0;
}
free(sb, M_DEVBUF);
}
static int
sb16_alloc_resources(struct sb_info *sb, device_t dev)
{
int rid;
rid = 0;
if (!sb->io_base)
sb->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
&rid, RF_ACTIVE);
rid = 0;
if (!sb->irq)
sb->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE);
rid = 0;
if (!sb->drq1)
sb->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid,
RF_ACTIVE);
rid = 1;
if (!sb->drq2)
sb->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid,
RF_ACTIVE);
if (sb->io_base && sb->drq1 && sb->irq) {
isa_dma_acquire(rman_get_start(sb->drq1));
isa_dmainit(rman_get_start(sb->drq1), sb->bufsize);
if (sb->drq2) {
isa_dma_acquire(rman_get_start(sb->drq2));
isa_dmainit(rman_get_start(sb->drq2), sb->bufsize);
} else {
sb->drq2 = sb->drq1;
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
}
return 0;
} else return ENXIO;
}
/* sbc does locking for us */
static void
sb_intr(void *arg)
{
struct sb_info *sb = (struct sb_info *)arg;
int reason, c;
/*
* The Vibra16X has separate flags for 8 and 16 bit transfers, but
* I have no idea how to tell capture from playback interrupts...
*/
reason = 0;
sb_lock(sb);
c = sb_getmixer(sb, IRQ_STAT);
if (c & 1)
sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */
if (c & 2)
sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */
sb_unlock(sb);
/*
* this tells us if the source is 8-bit or 16-bit dma. We
* have to check the io channel to map it to read or write...
*/
if (sb->bd_flags & BD_F_SB16X) {
if (c & 1) { /* 8-bit format */
if (sb->pch.fmt & AFMT_8BIT)
reason |= 1;
if (sb->rch.fmt & AFMT_8BIT)
reason |= 2;
}
if (c & 2) { /* 16-bit format */
if (sb->pch.fmt & AFMT_16BIT)
reason |= 1;
if (sb->rch.fmt & AFMT_16BIT)
reason |= 2;
}
} else {
if (c & 1) { /* 8-bit dma */
if (sb->pch.dch == 1)
reason |= 1;
if (sb->rch.dch == 1)
reason |= 2;
}
if (c & 2) { /* 16-bit dma */
if (sb->pch.dch == 2)
reason |= 1;
if (sb->rch.dch == 2)
reason |= 2;
}
}
#if 0
printf("sb_intr: reason=%d c=0x%x\n", reason, c);
#endif
if ((reason & 1) && (sb->pch.run))
chn_intr(sb->pch.channel);
if ((reason & 2) && (sb->rch.run))
chn_intr(sb->rch.channel);
}
static int
sb_setup(struct sb_info *sb)
{
struct sb_chinfo *ch;
u_int8_t v;
int l, pprio;
sb_lock(sb);
if (sb->bd_flags & BD_F_DMARUN)
sndbuf_dma(sb->pch.buffer, PCMTRIG_STOP);
if (sb->bd_flags & BD_F_DMARUN2)
sndbuf_dma(sb->rch.buffer, PCMTRIG_STOP);
sb->bd_flags &= ~(BD_F_DMARUN | BD_F_DMARUN2);
sb_reset_dsp(sb);
if (sb->bd_flags & BD_F_SB16X) {
/* full-duplex doesn't work! */
pprio = sb->pch.run? 1 : 0;
sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2);
sb->pch.dch = pprio? 1 : 0;
sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1);
sb->rch.dch = pprio? 2 : 1;
} else {
if (sb->pch.run && sb->rch.run) {
pprio = (sb->rch.fmt & AFMT_16BIT)? 0 : 1;
sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1);
sb->pch.dch = pprio? 2 : 1;
sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2);
sb->rch.dch = pprio? 1 : 2;
} else {
if (sb->pch.run) {
sndbuf_dmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1);
sb->pch.dch = (sb->pch.fmt & AFMT_16BIT)? 2 : 1;
sndbuf_dmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2);
sb->rch.dch = (sb->pch.fmt & AFMT_16BIT)? 1 : 2;
} else if (sb->rch.run) {
sndbuf_dmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2);
sb->pch.dch = (sb->rch.fmt & AFMT_16BIT)? 1 : 2;
sndbuf_dmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1);
sb->rch.dch = (sb->rch.fmt & AFMT_16BIT)? 2 : 1;
}
}
}
sndbuf_dmasetdir(sb->pch.buffer, PCMDIR_PLAY);
sndbuf_dmasetdir(sb->rch.buffer, PCMDIR_REC);
/*
printf("setup: [pch = %d, pfmt = %d, pgo = %d] [rch = %d, rfmt = %d, rgo = %d]\n",
sb->pch.dch, sb->pch.fmt, sb->pch.run, sb->rch.dch, sb->rch.fmt, sb->rch.run);
*/
ch = &sb->pch;
if (ch->run) {
l = ch->blksz;
if (ch->fmt & AFMT_16BIT)
l >>= 1;
l--;
/* play speed */
RANGE(ch->spd, 5000, 45000);
sb_cmd(sb, DSP_CMD_OUT16);
sb_cmd(sb, ch->spd >> 8);
sb_cmd(sb, ch->spd & 0xff);
/* play format, length */
v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC;
v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
sb_cmd(sb, v);
v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0;
v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
sb_cmd2(sb, v, l);
sndbuf_dma(ch->buffer, PCMTRIG_START);
sb->bd_flags |= BD_F_DMARUN;
}
ch = &sb->rch;
if (ch->run) {
l = ch->blksz;
if (ch->fmt & AFMT_16BIT)
l >>= 1;
l--;
/* record speed */
RANGE(ch->spd, 5000, 45000);
sb_cmd(sb, DSP_CMD_IN16);
sb_cmd(sb, ch->spd >> 8);
sb_cmd(sb, ch->spd & 0xff);
/* record format, length */
v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC;
v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
sb_cmd(sb, v);
v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0;
v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
sb_cmd2(sb, v, l);
sndbuf_dma(ch->buffer, PCMTRIG_START);
sb->bd_flags |= BD_F_DMARUN2;
}
sb_unlock(sb);
return 0;
}
/* channel interface */
static void *
sb16chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
{
struct sb_info *sb = devinfo;
struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch;
ch->parent = sb;
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
if (sndbuf_alloc(ch->buffer, sb->parent_dmat, 0, sb->bufsize) != 0)
return NULL;
return ch;
}
static int
sb16chan_setformat(kobj_t obj, void *data, u_int32_t format)
{
struct sb_chinfo *ch = data;
struct sb_info *sb = ch->parent;
ch->fmt = format;
sb->prio = ch->dir;
sb->prio16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
return 0;
}
static u_int32_t
sb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
{
struct sb_chinfo *ch = data;
ch->spd = speed;
return speed;
}
static u_int32_t
sb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct sb_chinfo *ch = data;
ch->blksz = blocksize;
return ch->blksz;
}
static int
sb16chan_trigger(kobj_t obj, void *data, int go)
{
struct sb_chinfo *ch = data;
struct sb_info *sb = ch->parent;
if (!PCMTRIG_COMMON(go))
return 0;
if (go == PCMTRIG_START)
ch->run = 1;
else
ch->run = 0;
sb_setup(sb);
return 0;
}
static u_int32_t
sb16chan_getptr(kobj_t obj, void *data)
{
struct sb_chinfo *ch = data;
return sndbuf_dmaptr(ch->buffer);
}
static struct pcmchan_caps *
sb16chan_getcaps(kobj_t obj, void *data)
{
struct sb_chinfo *ch = data;
struct sb_info *sb = ch->parent;
if ((sb->prio == 0) || (sb->prio == ch->dir))
return &sb16x_caps;
else
return sb->prio16? &sb16_caps8 : &sb16_caps16;
}
static int
sb16chan_resetdone(kobj_t obj, void *data)
{
struct sb_chinfo *ch = data;
struct sb_info *sb = ch->parent;
sb->prio = 0;
return 0;
}
static kobj_method_t sb16chan_methods[] = {
KOBJMETHOD(channel_init, sb16chan_init),
KOBJMETHOD(channel_resetdone, sb16chan_resetdone),
KOBJMETHOD(channel_setformat, sb16chan_setformat),
KOBJMETHOD(channel_setspeed, sb16chan_setspeed),
KOBJMETHOD(channel_setblocksize, sb16chan_setblocksize),
KOBJMETHOD(channel_trigger, sb16chan_trigger),
KOBJMETHOD(channel_getptr, sb16chan_getptr),
KOBJMETHOD(channel_getcaps, sb16chan_getcaps),
KOBJMETHOD_END
};
CHANNEL_DECLARE(sb16chan);
/************************************************************/
static int
sb16_probe(device_t dev)
{
char buf[64];
uintptr_t func, ver, f;
/* The parent device has already been probed. */
BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
if (func != SCF_PCM)
return (ENXIO);
BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
f = (ver & 0xffff0000) >> 16;
ver &= 0x0000ffff;
if (f & BD_F_SB16) {
snprintf(buf, sizeof buf, "SB16 DSP %d.%02d%s", (int) ver >> 8, (int) ver & 0xff,
(f & BD_F_SB16X)? " (ViBRA16X)" : "");
device_set_desc_copy(dev, buf);
return 0;
} else
return (ENXIO);
}
static int
sb16_attach(device_t dev)
{
struct sb_info *sb;
uintptr_t ver;
char status[SND_STATUSLEN], status2[SND_STATUSLEN];
gone_in_dev(dev, 14, "ISA sound driver");
sb = malloc(sizeof(*sb), M_DEVBUF, M_WAITOK | M_ZERO);
sb->parent_dev = device_get_parent(dev);
BUS_READ_IVAR(sb->parent_dev, dev, 1, &ver);
sb->bd_id = ver & 0x0000ffff;
sb->bd_flags = (ver & 0xffff0000) >> 16;
sb->bufsize = pcm_getbuffersize(dev, 4096, SB16_BUFFSIZE, 65536);
if (sb16_alloc_resources(sb, dev))
goto no;
sb_lock(sb);
if (sb_reset_dsp(sb)) {
sb_unlock(sb);
goto no;
}
sb_unlock(sb);
if (mixer_init(dev, &sb16mix_mixer_class, sb))
goto no;
if (snd_setup_intr(dev, sb->irq, 0, sb_intr, sb, &sb->ih))
goto no;
if (sb->bd_flags & BD_F_SB16X)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
sb->prio = 0;
if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
/*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sb->bufsize, /*nsegments*/1,
/*maxsegz*/0x3ffff, /*flags*/0,
/*lockfunc*/NULL, /*lockarg*/NULL,
&sb->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto no;
}
if (!(pcm_getflags(dev) & SD_F_SIMPLEX))
snprintf(status2, SND_STATUSLEN, ":%jd", rman_get_start(sb->drq2));
else
status2[0] = '\0';
snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd%s bufsz %u %s",
rman_get_start(sb->io_base), rman_get_start(sb->irq),
rman_get_start(sb->drq1), status2, sb->bufsize,
PCM_KLDSTRING(snd_sb16));
if (pcm_register(dev, sb, 1, 1))
goto no;
pcm_addchan(dev, PCMDIR_REC, &sb16chan_class, sb);
pcm_addchan(dev, PCMDIR_PLAY, &sb16chan_class, sb);
pcm_setstatus(dev, status);
return 0;
no:
sb16_release_resources(sb, dev);
return ENXIO;
}
static int
sb16_detach(device_t dev)
{
int r;
struct sb_info *sb;
r = pcm_unregister(dev);
if (r)
return r;
sb = pcm_getdevinfo(dev);
sb16_release_resources(sb, dev);
return 0;
}
static device_method_t sb16_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sb16_probe),
DEVMETHOD(device_attach, sb16_attach),
DEVMETHOD(device_detach, sb16_detach),
{ 0, 0 }
};
static driver_t sb16_driver = {
"pcm",
sb16_methods,
PCM_SOFTC_SIZE,
};
DRIVER_MODULE(snd_sb16, sbc, sb16_driver, 0, 0);
MODULE_DEPEND(snd_sb16, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
MODULE_DEPEND(snd_sb16, snd_sbc, 1, 1, 1);
MODULE_VERSION(snd_sb16, 1);

View File

@ -1,803 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
* Copyright (c) 1997,1998 Luigi Rizzo
*
* Derived from files in the Voxware 3.5 distribution,
* Copyright by Hannu Savolainen 1994, under the same copyright
* conditions.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/pcm/sound.h>
#include <dev/sound/isa/sb.h>
#include <dev/sound/chip.h>
#include <isa/isavar.h>
#include "mixer_if.h"
SND_DECLARE_FILE("$FreeBSD$");
#define SB_DEFAULT_BUFSZ 4096
static u_int32_t sb_fmt[] = {
SND_FORMAT(AFMT_U8, 1, 0),
0
};
static struct pcmchan_caps sb200_playcaps = {4000, 23000, sb_fmt, 0};
static struct pcmchan_caps sb200_reccaps = {4000, 13000, sb_fmt, 0};
static struct pcmchan_caps sb201_playcaps = {4000, 44100, sb_fmt, 0};
static struct pcmchan_caps sb201_reccaps = {4000, 15000, sb_fmt, 0};
static u_int32_t sbpro_fmt[] = {
SND_FORMAT(AFMT_U8, 1, 0),
SND_FORMAT(AFMT_U8, 2, 0),
0
};
static struct pcmchan_caps sbpro_playcaps = {4000, 44100, sbpro_fmt, 0};
static struct pcmchan_caps sbpro_reccaps = {4000, 44100, sbpro_fmt, 0};
struct sb_info;
struct sb_chinfo {
struct sb_info *parent;
struct pcm_channel *channel;
struct snd_dbuf *buffer;
int dir;
u_int32_t fmt, spd, blksz;
};
struct sb_info {
device_t parent_dev;
struct resource *io_base; /* I/O address for the board */
struct resource *irq;
struct resource *drq;
void *ih;
bus_dma_tag_t parent_dmat;
unsigned int bufsize;
int bd_id;
u_long bd_flags; /* board-specific flags */
struct sb_chinfo pch, rch;
};
static int sb_rd(struct sb_info *sb, int reg);
static void sb_wr(struct sb_info *sb, int reg, u_int8_t val);
static int sb_dspready(struct sb_info *sb);
static int sb_cmd(struct sb_info *sb, u_char val);
static int sb_cmd1(struct sb_info *sb, u_char cmd, int val);
static int sb_cmd2(struct sb_info *sb, u_char cmd, int val);
static u_int sb_get_byte(struct sb_info *sb);
static void sb_setmixer(struct sb_info *sb, u_int port, u_int value);
static int sb_getmixer(struct sb_info *sb, u_int port);
static int sb_reset_dsp(struct sb_info *sb);
static void sb_intr(void *arg);
static int sb_speed(struct sb_chinfo *ch);
static int sb_start(struct sb_chinfo *ch);
static int sb_stop(struct sb_chinfo *ch);
/*
* Common code for the midi and pcm functions
*
* sb_cmd write a single byte to the CMD port.
* sb_cmd1 write a CMD + 1 byte arg
* sb_cmd2 write a CMD + 2 byte arg
* sb_get_byte returns a single byte from the DSP data port
*/
static void
sb_lock(struct sb_info *sb) {
sbc_lock(device_get_softc(sb->parent_dev));
}
static void
sb_unlock(struct sb_info *sb) {
sbc_unlock(device_get_softc(sb->parent_dev));
}
static int
port_rd(struct resource *port, int off)
{
return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off);
}
static void
port_wr(struct resource *port, int off, u_int8_t data)
{
bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data);
}
static int
sb_rd(struct sb_info *sb, int reg)
{
return port_rd(sb->io_base, reg);
}
static void
sb_wr(struct sb_info *sb, int reg, u_int8_t val)
{
port_wr(sb->io_base, reg, val);
}
static int
sb_dspready(struct sb_info *sb)
{
return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0);
}
static int
sb_dspwr(struct sb_info *sb, u_char val)
{
int i;
for (i = 0; i < 1000; i++) {
if (sb_dspready(sb)) {
sb_wr(sb, SBDSP_CMD, val);
return 1;
}
if (i > 10) DELAY((i > 100)? 1000 : 10);
}
printf("sb_dspwr(0x%02x) timed out.\n", val);
return 0;
}
static int
sb_cmd(struct sb_info *sb, u_char val)
{
#if 0
printf("sb_cmd: %x\n", val);
#endif
return sb_dspwr(sb, val);
}
static int
sb_cmd1(struct sb_info *sb, u_char cmd, int val)
{
#if 0
printf("sb_cmd1: %x, %x\n", cmd, val);
#endif
if (sb_dspwr(sb, cmd)) {
return sb_dspwr(sb, val & 0xff);
} else return 0;
}
static int
sb_cmd2(struct sb_info *sb, u_char cmd, int val)
{
#if 0
printf("sb_cmd2: %x, %x\n", cmd, val);
#endif
if (sb_dspwr(sb, cmd)) {
return sb_dspwr(sb, val & 0xff) &&
sb_dspwr(sb, (val >> 8) & 0xff);
} else return 0;
}
/*
* in the SB, there is a set of indirect "mixer" registers with
* address at offset 4, data at offset 5
*
* we don't need to interlock these, the mixer lock will suffice.
*/
static void
sb_setmixer(struct sb_info *sb, u_int port, u_int value)
{
sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff));
DELAY(10);
}
static int
sb_getmixer(struct sb_info *sb, u_int port)
{
int val;
sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
val = sb_rd(sb, SB_MIX_DATA);
DELAY(10);
return val;
}
static u_int
sb_get_byte(struct sb_info *sb)
{
int i;
for (i = 1000; i > 0; i--) {
if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80)
return sb_rd(sb, DSP_READ);
else
DELAY(20);
}
return 0xffff;
}
static int
sb_reset_dsp(struct sb_info *sb)
{
sb_wr(sb, SBDSP_RST, 3);
DELAY(100);
sb_wr(sb, SBDSP_RST, 0);
if (sb_get_byte(sb) != 0xAA) {
DEB(printf("sb_reset_dsp 0x%lx failed\n",
rman_get_start(sb->io_base)));
return ENXIO; /* Sorry */
}
return 0;
}
static void
sb_release_resources(struct sb_info *sb, device_t dev)
{
if (sb->irq) {
if (sb->ih)
bus_teardown_intr(dev, sb->irq, sb->ih);
bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq);
sb->irq = NULL;
}
if (sb->drq) {
isa_dma_release(rman_get_start(sb->drq));
bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq);
sb->drq = NULL;
}
if (sb->io_base) {
bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base);
sb->io_base = NULL;
}
if (sb->parent_dmat) {
bus_dma_tag_destroy(sb->parent_dmat);
sb->parent_dmat = 0;
}
free(sb, M_DEVBUF);
}
static int
sb_alloc_resources(struct sb_info *sb, device_t dev)
{
int rid;
rid = 0;
if (!sb->io_base)
sb->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
&rid, RF_ACTIVE);
rid = 0;
if (!sb->irq)
sb->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&rid, RF_ACTIVE);
rid = 0;
if (!sb->drq)
sb->drq = bus_alloc_resource_any(dev, SYS_RES_DRQ,
&rid, RF_ACTIVE);
if (sb->io_base && sb->drq && sb->irq) {
isa_dma_acquire(rman_get_start(sb->drq));
isa_dmainit(rman_get_start(sb->drq), sb->bufsize);
return 0;
} else return ENXIO;
}
/************************************************************/
static int
sbpromix_init(struct snd_mixer *m)
{
struct sb_info *sb = mix_getdevinfo(m);
mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE |
SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME);
mix_setrecdevs(m, SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD);
sb_setmixer(sb, 0, 1); /* reset mixer */
return 0;
}
static int
sbpromix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct sb_info *sb = mix_getdevinfo(m);
int reg, max;
u_char val;
max = 7;
switch (dev) {
case SOUND_MIXER_PCM:
reg = 0x04;
break;
case SOUND_MIXER_MIC:
reg = 0x0a;
max = 3;
break;
case SOUND_MIXER_VOLUME:
reg = 0x22;
break;
case SOUND_MIXER_SYNTH:
reg = 0x26;
break;
case SOUND_MIXER_CD:
reg = 0x28;
break;
case SOUND_MIXER_LINE:
reg = 0x2e;
break;
default:
return -1;
}
left = (left * max) / 100;
right = (dev == SOUND_MIXER_MIC)? left : ((right * max) / 100);
val = (dev == SOUND_MIXER_MIC)? (left << 1) : (left << 5 | right << 1);
sb_setmixer(sb, reg, val);
left = (left * 100) / max;
right = (right * 100) / max;
return left | (right << 8);
}
static u_int32_t
sbpromix_setrecsrc(struct snd_mixer *m, u_int32_t src)
{
struct sb_info *sb = mix_getdevinfo(m);
u_char recdev;
if (src == SOUND_MASK_LINE)
recdev = 0x06;
else if (src == SOUND_MASK_CD)
recdev = 0x02;
else { /* default: mic */
src = SOUND_MASK_MIC;
recdev = 0;
}
sb_setmixer(sb, RECORD_SRC, recdev | (sb_getmixer(sb, RECORD_SRC) & ~0x07));
return src;
}
static kobj_method_t sbpromix_mixer_methods[] = {
KOBJMETHOD(mixer_init, sbpromix_init),
KOBJMETHOD(mixer_set, sbpromix_set),
KOBJMETHOD(mixer_setrecsrc, sbpromix_setrecsrc),
KOBJMETHOD_END
};
MIXER_DECLARE(sbpromix_mixer);
/************************************************************/
static int
sbmix_init(struct snd_mixer *m)
{
struct sb_info *sb = mix_getdevinfo(m);
mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_CD | SOUND_MASK_VOLUME);
mix_setrecdevs(m, 0);
sb_setmixer(sb, 0, 1); /* reset mixer */
return 0;
}
static int
sbmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
{
struct sb_info *sb = mix_getdevinfo(m);
int reg, max;
max = 7;
switch (dev) {
case SOUND_MIXER_VOLUME:
reg = 0x2;
break;
case SOUND_MIXER_SYNTH:
reg = 0x6;
break;
case SOUND_MIXER_CD:
reg = 0x8;
break;
case SOUND_MIXER_PCM:
reg = 0x0a;
max = 3;
break;
default:
return -1;
}
left = (left * max) / 100;
sb_setmixer(sb, reg, left << 1);
left = (left * 100) / max;
return left | (left << 8);
}
static u_int32_t
sbmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
{
return 0;
}
static kobj_method_t sbmix_mixer_methods[] = {
KOBJMETHOD(mixer_init, sbmix_init),
KOBJMETHOD(mixer_set, sbmix_set),
KOBJMETHOD(mixer_setrecsrc, sbmix_setrecsrc),
KOBJMETHOD_END
};
MIXER_DECLARE(sbmix_mixer);
/************************************************************/
static void
sb_intr(void *arg)
{
struct sb_info *sb = (struct sb_info *)arg;
sb_lock(sb);
if (sndbuf_runsz(sb->pch.buffer) > 0) {
sb_unlock(sb);
chn_intr(sb->pch.channel);
sb_lock(sb);
}
if (sndbuf_runsz(sb->rch.buffer) > 0) {
sb_unlock(sb);
chn_intr(sb->rch.channel);
sb_lock(sb);
}
sb_rd(sb, DSP_DATA_AVAIL); /* int ack */
sb_unlock(sb);
}
static int
sb_speed(struct sb_chinfo *ch)
{
struct sb_info *sb = ch->parent;
int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
int speed, tmp, thresh, max;
u_char tconst;
if (sb->bd_id >= 0x300) {
thresh = stereo? 11025 : 23000;
max = stereo? 22050 : 44100;
} else if (sb->bd_id > 0x200) {
thresh = play? 23000 : 13000;
max = play? 44100 : 15000;
} else {
thresh = 999999;
max = play? 23000 : 13000;
}
speed = ch->spd;
if (speed > max)
speed = max;
sb_lock(sb);
sb->bd_flags &= ~BD_F_HISPEED;
if (speed > thresh)
sb->bd_flags |= BD_F_HISPEED;
tmp = 65536 - (256000000 / (speed << stereo));
tconst = tmp >> 8;
sb_cmd1(sb, 0x40, tconst); /* set time constant */
speed = (256000000 / (65536 - tmp)) >> stereo;
ch->spd = speed;
sb_unlock(sb);
return speed;
}
static int
sb_start(struct sb_chinfo *ch)
{
struct sb_info *sb = ch->parent;
int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
int l = ch->blksz;
u_char i;
l--;
sb_lock(sb);
if (play)
sb_cmd(sb, DSP_CMD_SPKON);
if (sb->bd_flags & BD_F_HISPEED)
i = play? 0x90 : 0x98;
else
i = play? 0x1c : 0x2c;
sb_setmixer(sb, 0x0e, stereo? 2 : 0);
sb_cmd2(sb, 0x48, l);
sb_cmd(sb, i);
sb->bd_flags |= BD_F_DMARUN;
sb_unlock(sb);
return 0;
}
static int
sb_stop(struct sb_chinfo *ch)
{
struct sb_info *sb = ch->parent;
int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
sb_lock(sb);
if (sb->bd_flags & BD_F_HISPEED)
sb_reset_dsp(sb);
else {
#if 0
/*
* NOTE: DSP_CMD_DMAEXIT_8 does not work with old
* soundblaster.
*/
sb_cmd(sb, DSP_CMD_DMAEXIT_8);
#endif
sb_reset_dsp(sb);
}
if (play)
sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */
sb_unlock(sb);
sb->bd_flags &= ~BD_F_DMARUN;
return 0;
}
/* channel interface */
static void *
sbchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
{
struct sb_info *sb = devinfo;
struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch;
ch->parent = sb;
ch->channel = c;
ch->dir = dir;
ch->buffer = b;
if (sndbuf_alloc(ch->buffer, sb->parent_dmat, 0, sb->bufsize) != 0)
return NULL;
sndbuf_dmasetup(ch->buffer, sb->drq);
return ch;
}
static int
sbchan_setformat(kobj_t obj, void *data, u_int32_t format)
{
struct sb_chinfo *ch = data;
ch->fmt = format;
return 0;
}
static u_int32_t
sbchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
{
struct sb_chinfo *ch = data;
ch->spd = speed;
return sb_speed(ch);
}
static u_int32_t
sbchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
struct sb_chinfo *ch = data;
ch->blksz = blocksize;
return ch->blksz;
}
static int
sbchan_trigger(kobj_t obj, void *data, int go)
{
struct sb_chinfo *ch = data;
if (!PCMTRIG_COMMON(go))
return 0;
sndbuf_dma(ch->buffer, go);
if (go == PCMTRIG_START)
sb_start(ch);
else
sb_stop(ch);
return 0;
}
static u_int32_t
sbchan_getptr(kobj_t obj, void *data)
{
struct sb_chinfo *ch = data;
return sndbuf_dmaptr(ch->buffer);
}
static struct pcmchan_caps *
sbchan_getcaps(kobj_t obj, void *data)
{
struct sb_chinfo *ch = data;
int p = (ch->dir == PCMDIR_PLAY)? 1 : 0;
if (ch->parent->bd_id == 0x200)
return p? &sb200_playcaps : &sb200_reccaps;
if (ch->parent->bd_id < 0x300)
return p? &sb201_playcaps : &sb201_reccaps;
return p? &sbpro_playcaps : &sbpro_reccaps;
}
static kobj_method_t sbchan_methods[] = {
KOBJMETHOD(channel_init, sbchan_init),
KOBJMETHOD(channel_setformat, sbchan_setformat),
KOBJMETHOD(channel_setspeed, sbchan_setspeed),
KOBJMETHOD(channel_setblocksize, sbchan_setblocksize),
KOBJMETHOD(channel_trigger, sbchan_trigger),
KOBJMETHOD(channel_getptr, sbchan_getptr),
KOBJMETHOD(channel_getcaps, sbchan_getcaps),
KOBJMETHOD_END
};
CHANNEL_DECLARE(sbchan);
/************************************************************/
static int
sb_probe(device_t dev)
{
char buf[64];
uintptr_t func, ver, f;
/* The parent device has already been probed. */
BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
if (func != SCF_PCM)
return (ENXIO);
BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
f = (ver & 0xffff0000) >> 16;
ver &= 0x0000ffff;
if ((f & BD_F_ESS) || (ver >= 0x400))
return (ENXIO);
snprintf(buf, sizeof buf, "SB DSP %d.%02d", (int) ver >> 8, (int) ver & 0xff);
device_set_desc_copy(dev, buf);
return 0;
}
static int
sb_attach(device_t dev)
{
struct sb_info *sb;
char status[SND_STATUSLEN];
uintptr_t ver;
gone_in_dev(dev, 14, "ISA sound driver");
sb = malloc(sizeof(*sb), M_DEVBUF, M_WAITOK | M_ZERO);
sb->parent_dev = device_get_parent(dev);
BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
sb->bd_id = ver & 0x0000ffff;
sb->bd_flags = (ver & 0xffff0000) >> 16;
sb->bufsize = pcm_getbuffersize(dev, 4096, SB_DEFAULT_BUFSZ, 65536);
if (sb_alloc_resources(sb, dev))
goto no;
if (sb_reset_dsp(sb))
goto no;
if (mixer_init(dev, (sb->bd_id < 0x300)? &sbmix_mixer_class : &sbpromix_mixer_class, sb))
goto no;
if (snd_setup_intr(dev, sb->irq, 0, sb_intr, sb, &sb->ih))
goto no;
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
/*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/sb->bufsize, /*nsegments*/1,
/*maxsegz*/0x3ffff, /*flags*/0,
/*lockfunc*/NULL, /*lockarg*/NULL,
&sb->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
goto no;
}
snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd bufsz %u %s",
rman_get_start(sb->io_base), rman_get_start(sb->irq),
rman_get_start(sb->drq), sb->bufsize, PCM_KLDSTRING(snd_sb8));
if (pcm_register(dev, sb, 1, 1))
goto no;
pcm_addchan(dev, PCMDIR_REC, &sbchan_class, sb);
pcm_addchan(dev, PCMDIR_PLAY, &sbchan_class, sb);
pcm_setstatus(dev, status);
return 0;
no:
sb_release_resources(sb, dev);
return ENXIO;
}
static int
sb_detach(device_t dev)
{
int r;
struct sb_info *sb;
r = pcm_unregister(dev);
if (r)
return r;
sb = pcm_getdevinfo(dev);
sb_release_resources(sb, dev);
return 0;
}
static device_method_t sb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sb_probe),
DEVMETHOD(device_attach, sb_attach),
DEVMETHOD(device_detach, sb_detach),
{ 0, 0 }
};
static driver_t sb_driver = {
"pcm",
sb_methods,
PCM_SOFTC_SIZE,
};
DRIVER_MODULE(snd_sb8, sbc, sb_driver, 0, 0);
MODULE_DEPEND(snd_sb8, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
MODULE_DEPEND(snd_sb8, snd_sbc, 1, 1, 1);
MODULE_VERSION(snd_sb8, 1);

View File

@ -1,752 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 1999 Seigo Tanimura
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/isa/sb.h>
#include <isa/isavar.h>
SND_DECLARE_FILE("$FreeBSD$");
#define IO_MAX 3
#define IRQ_MAX 1
#define DRQ_MAX 2
#define INTR_MAX 2
struct sbc_softc;
struct sbc_ihl {
driver_intr_t *intr[INTR_MAX];
void *intr_arg[INTR_MAX];
struct sbc_softc *parent;
};
/* Here is the parameter structure per a device. */
struct sbc_softc {
device_t dev; /* device */
device_t child_pcm, child_midi1, child_midi2;
int io_rid[IO_MAX]; /* io port rids */
struct resource *io[IO_MAX]; /* io port resources */
int io_alloced[IO_MAX]; /* io port alloc flag */
int irq_rid[IRQ_MAX]; /* irq rids */
struct resource *irq[IRQ_MAX]; /* irq resources */
int irq_alloced[IRQ_MAX]; /* irq alloc flag */
int drq_rid[DRQ_MAX]; /* drq rids */
struct resource *drq[DRQ_MAX]; /* drq resources */
int drq_alloced[DRQ_MAX]; /* drq alloc flag */
struct sbc_ihl ihl[IRQ_MAX];
void *ih[IRQ_MAX];
struct mtx *lock;
u_int32_t bd_ver;
};
static int sbc_probe(device_t dev);
static int sbc_attach(device_t dev);
static void sbc_intr(void *p);
static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags);
static int sbc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
static int sbc_setup_intr(device_t dev, device_t child, struct resource *irq,
int flags,
driver_filter_t *filter,
driver_intr_t *intr,
void *arg, void **cookiep);
static int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq,
void *cookie);
static int alloc_resource(struct sbc_softc *scp);
static int release_resource(struct sbc_softc *scp);
static int io_range[3] = {0x10, 0x2, 0x4};
static int sb_rd(struct resource *io, int reg);
static void sb_wr(struct resource *io, int reg, u_int8_t val);
static int sb_dspready(struct resource *io);
static int sb_cmd(struct resource *io, u_char val);
static u_int sb_get_byte(struct resource *io);
static void sb_setmixer(struct resource *io, u_int port, u_int value);
static void
sbc_lockinit(struct sbc_softc *scp)
{
scp->lock = snd_mtxcreate(device_get_nameunit(scp->dev),
"snd_sbc softc");
}
static void
sbc_lockdestroy(struct sbc_softc *scp)
{
snd_mtxfree(scp->lock);
}
void
sbc_lock(struct sbc_softc *scp)
{
snd_mtxlock(scp->lock);
}
void
sbc_lockassert(struct sbc_softc *scp)
{
snd_mtxassert(scp->lock);
}
void
sbc_unlock(struct sbc_softc *scp)
{
snd_mtxunlock(scp->lock);
}
static int
sb_rd(struct resource *io, int reg)
{
return bus_space_read_1(rman_get_bustag(io),
rman_get_bushandle(io),
reg);
}
static void
sb_wr(struct resource *io, int reg, u_int8_t val)
{
bus_space_write_1(rman_get_bustag(io),
rman_get_bushandle(io),
reg, val);
}
static int
sb_dspready(struct resource *io)
{
return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0);
}
static int
sb_dspwr(struct resource *io, u_char val)
{
int i;
for (i = 0; i < 1000; i++) {
if (sb_dspready(io)) {
sb_wr(io, SBDSP_CMD, val);
return 1;
}
if (i > 10) DELAY((i > 100)? 1000 : 10);
}
printf("sb_dspwr(0x%02x) timed out.\n", val);
return 0;
}
static int
sb_cmd(struct resource *io, u_char val)
{
return sb_dspwr(io, val);
}
static void
sb_setmixer(struct resource *io, u_int port, u_int value)
{
u_long flags;
flags = spltty();
sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
DELAY(10);
sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff));
DELAY(10);
splx(flags);
}
static u_int
sb_get_byte(struct resource *io)
{
int i;
for (i = 1000; i > 0; i--) {
if (sb_rd(io, DSP_DATA_AVAIL) & 0x80)
return sb_rd(io, DSP_READ);
else
DELAY(20);
}
return 0xffff;
}
static int
sb_reset_dsp(struct resource *io)
{
sb_wr(io, SBDSP_RST, 3);
DELAY(100);
sb_wr(io, SBDSP_RST, 0);
return (sb_get_byte(io) == 0xAA)? 0 : ENXIO;
}
static int
sb_identify_board(struct resource *io)
{
int ver, essver, rev;
sb_cmd(io, DSP_CMD_GETVER); /* Get version */
ver = (sb_get_byte(io) << 8) | sb_get_byte(io);
if (ver < 0x100 || ver > 0x4ff) return 0;
if (ver == 0x0301) {
/* Try to detect ESS chips. */
sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */
essver = (sb_get_byte(io) << 8) | sb_get_byte(io);
rev = essver & 0x000f;
essver &= 0xfff0;
if (essver == 0x4880) ver |= 0x1000;
else if (essver == 0x6880) ver = 0x0500 | rev;
}
return ver;
}
static struct isa_pnp_id sbc_ids[] = {
{0x01008c0e, "Creative ViBRA16C"}, /* CTL0001 */
{0x31008c0e, "Creative SB16/SB32"}, /* CTL0031 */
{0x41008c0e, "Creative SB16/SB32"}, /* CTL0041 */
{0x42008c0e, "Creative SB AWE64"}, /* CTL0042 */
{0x43008c0e, "Creative ViBRA16X"}, /* CTL0043 */
{0x44008c0e, "Creative SB AWE64 Gold"}, /* CTL0044 */
{0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */
{0x46008c0e, "Creative SB AWE64"}, /* CTL0046 */
{0x01000000, "Avance Logic ALS100+"}, /* @@@0001 - ViBRA16X clone */
{0x01100000, "Avance Asound 110"}, /* @@@1001 */
{0x01200000, "Avance Logic ALS120"}, /* @@@2001 - ViBRA16X clone */
{0x81167316, "ESS ES1681"}, /* ESS1681 */
{0x02017316, "ESS ES1688"}, /* ESS1688 */
{0x68097316, "ESS ES1688"}, /* ESS1688 */
{0x68187316, "ESS ES1868"}, /* ESS1868 */
{0x03007316, "ESS ES1869"}, /* ESS1869 */
{0x69187316, "ESS ES1869"}, /* ESS1869 */
{0xabb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ab */
{0xacb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ac */
{0x78187316, "ESS ES1878"}, /* ESS1878 */
{0x79187316, "ESS ES1879"}, /* ESS1879 */
{0x88187316, "ESS ES1888"}, /* ESS1888 */
{0x07017316, "ESS ES1888 (DEC OEM)"}, /* ESS0107 */
{0x06017316, "ESS ES1888 (Dell OEM)"}, /* ESS0106 */
{0}
};
static int
sbc_probe(device_t dev)
{
char *s = NULL;
u_int32_t lid, vid;
lid = isa_get_logicalid(dev);
vid = isa_get_vendorid(dev);
if (lid) {
if (lid == 0x01000000 && vid != 0x01009305) /* ALS0001 */
return ENXIO;
/* Check pnp ids */
return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids);
} else {
int rid = 0, ver;
struct resource *io;
io = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid,
16, RF_ACTIVE);
if (!io) goto bad;
if (sb_reset_dsp(io)) goto bad2;
ver = sb_identify_board(io);
if (ver == 0) goto bad2;
switch ((ver & 0x00000f00) >> 8) {
case 1:
device_set_desc(dev, "SoundBlaster 1.0 (not supported)");
s = NULL;
break;
case 2:
s = "SoundBlaster 2.0";
break;
case 3:
s = (ver & 0x0000f000)? "ESS 488" : "SoundBlaster Pro";
break;
case 4:
s = "SoundBlaster 16";
break;
case 5:
s = (ver & 0x00000008)? "ESS 688" : "ESS 1688";
break;
}
if (s) device_set_desc(dev, s);
bad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io);
bad: return s? 0 : ENXIO;
}
}
static int
sbc_attach(device_t dev)
{
char *err = NULL;
struct sbc_softc *scp;
struct sndcard_func *func;
u_int32_t logical_id = isa_get_logicalid(dev);
int flags = device_get_flags(dev);
int f, dh, dl, x, irq, i;
gone_in_dev(dev, 14, "ISA sound driver");
if (!logical_id && (flags & DV_F_DUAL_DMA)) {
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
}
scp = device_get_softc(dev);
bzero(scp, sizeof(*scp));
scp->dev = dev;
sbc_lockinit(scp);
err = "alloc_resource";
if (alloc_resource(scp)) goto bad;
err = "sb_reset_dsp";
if (sb_reset_dsp(scp->io[0])) goto bad;
err = "sb_identify_board";
scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff;
if (scp->bd_ver == 0) goto bad;
f = 0;
if (logical_id == 0x01200000 && scp->bd_ver < 0x0400) scp->bd_ver = 0x0499;
switch ((scp->bd_ver & 0x0f00) >> 8) {
case 1: /* old sound blaster has nothing... */
break;
case 2:
f |= BD_F_DUP_MIDI;
if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335;
break;
case 5:
f |= BD_F_ESS;
scp->bd_ver = 0x0301;
case 3:
f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345;
break;
case 4:
f |= BD_F_SB16 | BD_F_MIX_CT1745;
if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1;
if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl;
if (!logical_id && (dh < dl)) {
struct resource *r;
r = scp->drq[0];
scp->drq[0] = scp->drq[1];
scp->drq[1] = r;
dl = rman_get_start(scp->drq[0]);
dh = rman_get_start(scp->drq[1]);
}
/* soft irq/dma configuration */
x = -1;
irq = rman_get_start(scp->irq[0]);
if (irq == 5) x = 2;
else if (irq == 7) x = 4;
else if (irq == 9) x = 1;
else if (irq == 10) x = 8;
if (x == -1) {
err = "bad irq (5/7/9/10 valid)";
goto bad;
}
else sb_setmixer(scp->io[0], IRQ_NR, x);
sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl));
if (bootverbose) {
device_printf(dev, "setting card to irq %d, drq %d", irq, dl);
if (dl != dh) printf(", %d", dh);
printf("\n");
}
break;
}
switch (logical_id) {
case 0x43008c0e: /* CTL0043 */
case 0x01200000:
case 0x01000000:
f |= BD_F_SB16X;
break;
}
scp->bd_ver |= f << 16;
err = "setup_intr";
for (i = 0; i < IRQ_MAX; i++) {
scp->ihl[i].parent = scp;
if (snd_setup_intr(dev, scp->irq[i], 0, sbc_intr, &scp->ihl[i], &scp->ih[i]))
goto bad;
}
/* PCM Audio */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
if (func == NULL) goto bad;
func->func = SCF_PCM;
scp->child_pcm = device_add_child(dev, "pcm", -1);
device_set_ivars(scp->child_pcm, func);
/* Midi Interface */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
if (func == NULL) goto bad;
func->func = SCF_MIDI;
scp->child_midi1 = device_add_child(dev, "midi", -1);
device_set_ivars(scp->child_midi1, func);
/* OPL FM Synthesizer */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
if (func == NULL) goto bad;
func->func = SCF_SYNTH;
scp->child_midi2 = device_add_child(dev, "midi", -1);
device_set_ivars(scp->child_midi2, func);
/* probe/attach kids */
bus_generic_attach(dev);
return (0);
bad: if (err) device_printf(dev, "%s\n", err);
release_resource(scp);
return (ENXIO);
}
static int
sbc_detach(device_t dev)
{
struct sbc_softc *scp = device_get_softc(dev);
sbc_lock(scp);
device_delete_child(dev, scp->child_midi2);
device_delete_child(dev, scp->child_midi1);
device_delete_child(dev, scp->child_pcm);
release_resource(scp);
sbc_lockdestroy(scp);
return bus_generic_detach(dev);
}
static void
sbc_intr(void *p)
{
struct sbc_ihl *ihl = p;
int i;
/* sbc_lock(ihl->parent); */
i = 0;
while (i < INTR_MAX) {
if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]);
i++;
}
/* sbc_unlock(ihl->parent); */
}
static int
sbc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags,
driver_filter_t *filter,
driver_intr_t *intr,
void *arg, void **cookiep)
{
struct sbc_softc *scp = device_get_softc(dev);
struct sbc_ihl *ihl = NULL;
int i, ret;
if (filter != NULL) {
printf("sbc.c: we cannot use a filter here\n");
return (EINVAL);
}
sbc_lock(scp);
i = 0;
while (i < IRQ_MAX) {
if (irq == scp->irq[i]) ihl = &scp->ihl[i];
i++;
}
ret = 0;
if (ihl == NULL) ret = EINVAL;
i = 0;
while ((ret == 0) && (i < INTR_MAX)) {
if (ihl->intr[i] == NULL) {
ihl->intr[i] = intr;
ihl->intr_arg[i] = arg;
*cookiep = &ihl->intr[i];
ret = -1;
} else i++;
}
sbc_unlock(scp);
return (ret > 0)? EINVAL : 0;
}
static int
sbc_teardown_intr(device_t dev, device_t child, struct resource *irq,
void *cookie)
{
struct sbc_softc *scp = device_get_softc(dev);
struct sbc_ihl *ihl = NULL;
int i, ret;
sbc_lock(scp);
i = 0;
while (i < IRQ_MAX) {
if (irq == scp->irq[i]) ihl = &scp->ihl[i];
i++;
}
ret = 0;
if (ihl == NULL) ret = EINVAL;
i = 0;
while ((ret == 0) && (i < INTR_MAX)) {
if (cookie == &ihl->intr[i]) {
ihl->intr[i] = NULL;
ihl->intr_arg[i] = NULL;
return 0;
} else i++;
}
sbc_unlock(scp);
return (ret > 0)? EINVAL : 0;
}
static struct resource *
sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct sbc_softc *scp;
int *alloced, rid_max, alloced_max;
struct resource **res;
scp = device_get_softc(bus);
switch (type) {
case SYS_RES_IOPORT:
alloced = scp->io_alloced;
res = scp->io;
rid_max = IO_MAX - 1;
alloced_max = 1;
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
res = scp->drq;
rid_max = DRQ_MAX - 1;
alloced_max = 1;
break;
case SYS_RES_IRQ:
alloced = scp->irq_alloced;
res = scp->irq;
rid_max = IRQ_MAX - 1;
alloced_max = INTR_MAX; /* pcm and mpu may share the irq. */
break;
default:
return (NULL);
}
if (*rid > rid_max || alloced[*rid] == alloced_max)
return (NULL);
alloced[*rid]++;
return (res[*rid]);
}
static int
sbc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
struct sbc_softc *scp;
int *alloced, rid_max;
scp = device_get_softc(bus);
switch (type) {
case SYS_RES_IOPORT:
alloced = scp->io_alloced;
rid_max = IO_MAX - 1;
break;
case SYS_RES_DRQ:
alloced = scp->drq_alloced;
rid_max = DRQ_MAX - 1;
break;
case SYS_RES_IRQ:
alloced = scp->irq_alloced;
rid_max = IRQ_MAX - 1;
break;
default:
return (1);
}
if (rid > rid_max || alloced[rid] == 0)
return (1);
alloced[rid]--;
return (0);
}
static int
sbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
{
struct sbc_softc *scp = device_get_softc(bus);
struct sndcard_func *func = device_get_ivars(dev);
switch (index) {
case 0:
*result = func->func;
break;
case 1:
*result = scp->bd_ver;
break;
default:
return ENOENT;
}
return 0;
}
static int
sbc_write_ivar(device_t bus, device_t dev,
int index, uintptr_t value)
{
switch (index) {
case 0:
case 1:
return EINVAL;
default:
return (ENOENT);
}
}
static int
alloc_resource(struct sbc_softc *scp)
{
int i;
for (i = 0 ; i < IO_MAX ; i++) {
if (scp->io[i] == NULL) {
scp->io_rid[i] = i;
scp->io[i] = bus_alloc_resource_anywhere(scp->dev,
SYS_RES_IOPORT,
&scp->io_rid[i],
io_range[i],
RF_ACTIVE);
if (i == 0 && scp->io[i] == NULL)
return (1);
scp->io_alloced[i] = 0;
}
}
for (i = 0 ; i < DRQ_MAX ; i++) {
if (scp->drq[i] == NULL) {
scp->drq_rid[i] = i;
scp->drq[i] = bus_alloc_resource_any(scp->dev,
SYS_RES_DRQ,
&scp->drq_rid[i],
RF_ACTIVE);
if (i == 0 && scp->drq[i] == NULL)
return (1);
scp->drq_alloced[i] = 0;
}
}
for (i = 0 ; i < IRQ_MAX ; i++) {
if (scp->irq[i] == NULL) {
scp->irq_rid[i] = i;
scp->irq[i] = bus_alloc_resource_any(scp->dev,
SYS_RES_IRQ,
&scp->irq_rid[i],
RF_ACTIVE);
if (i == 0 && scp->irq[i] == NULL)
return (1);
scp->irq_alloced[i] = 0;
}
}
return (0);
}
static int
release_resource(struct sbc_softc *scp)
{
int i;
for (i = 0 ; i < IO_MAX ; i++) {
if (scp->io[i] != NULL) {
bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]);
scp->io[i] = NULL;
}
}
for (i = 0 ; i < DRQ_MAX ; i++) {
if (scp->drq[i] != NULL) {
bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]);
scp->drq[i] = NULL;
}
}
for (i = 0 ; i < IRQ_MAX ; i++) {
if (scp->irq[i] != NULL) {
if (scp->ih[i] != NULL)
bus_teardown_intr(scp->dev, scp->irq[i], scp->ih[i]);
scp->ih[i] = NULL;
bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid[i], scp->irq[i]);
scp->irq[i] = NULL;
}
}
return (0);
}
static device_method_t sbc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sbc_probe),
DEVMETHOD(device_attach, sbc_attach),
DEVMETHOD(device_detach, sbc_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* Bus interface */
DEVMETHOD(bus_read_ivar, sbc_read_ivar),
DEVMETHOD(bus_write_ivar, sbc_write_ivar),
DEVMETHOD(bus_alloc_resource, sbc_alloc_resource),
DEVMETHOD(bus_release_resource, sbc_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_setup_intr, sbc_setup_intr),
DEVMETHOD(bus_teardown_intr, sbc_teardown_intr),
DEVMETHOD_END
};
static driver_t sbc_driver = {
"sbc",
sbc_methods,
sizeof(struct sbc_softc),
};
/* sbc can be attached to an isa bus. */
DRIVER_MODULE(snd_sbc, isa, sbc_driver, 0, 0);
DRIVER_MODULE(snd_sbc, acpi, sbc_driver, 0, 0);
MODULE_DEPEND(snd_sbc, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
MODULE_VERSION(snd_sbc, 1);
ISA_PNP_INFO(sbc_ids);

View File

@ -8,7 +8,7 @@ SYSDIR?=${SRCTOP}/sys
SUBDIR= als4000 atiixp cs4281 ${_csa} emu10k1 emu10kx
SUBDIR+= envy24 envy24ht es137x fm801 hda hdspe ich
SUBDIR+= ${_maestro3} neomagic sb16 sb8 sbc solo spicds t4dwave via8233
SUBDIR+= ${_maestro3} neomagic solo spicds t4dwave via8233
SUBDIR+= via82c686 vibes driver uaudio
.if ${MK_SOURCELESS_UCODE} != "no"

View File

@ -1,9 +0,0 @@
# $FreeBSD$
.PATH: ${SRCTOP}/sys/dev/sound/isa
KMOD= snd_sb16
SRCS= device_if.h bus_if.h isa_if.h pci_if.h
SRCS+= sb16.c
.include <bsd.kmod.mk>

View File

@ -1,9 +0,0 @@
# $FreeBSD$
.PATH: ${SRCTOP}/sys/dev/sound/isa
KMOD= snd_sb8
SRCS= device_if.h bus_if.h isa_if.h pci_if.h
SRCS+= sb8.c
.include <bsd.kmod.mk>

View File

@ -1,11 +0,0 @@
# $FreeBSD$
.PATH: ${SRCTOP}/sys/dev/sound/isa
KMOD= snd_sbc
SRCS= device_if.h bus_if.h isa_if.h pci_if.h
SRCS+= sbc.c
EXPORT_SYMS= YES
.include <bsd.kmod.mk>