use a global devclass for all drivers - i'm not entirely sure why this
worked before. mixer, dsp and sndstat are seperate devices - give them their own cdevsws instead of demuxing requests sent to a single cdevsw. use the si_drv1/si_drv2 fields in dev_t structures for holding information specific to an open instance of mixer/dsp. nuke /dev/{dsp,dspW,audio}[0-9]* links - this functionality is now provided using cloning. various locking fixes.
This commit is contained in:
parent
b81ba90a5f
commit
4a0664e88e
@ -67,8 +67,6 @@ static int ad1816_wait_init(struct ad1816_info *ad1816, int x);
|
||||
static u_short ad1816_read(struct ad1816_info *ad1816, u_int reg);
|
||||
static void ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data);
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
static u_int32_t ad1816_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
|
@ -168,8 +168,6 @@ static driver_t es1888_driver = {
|
||||
1, /* no softc */
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_es1888, isa, es1888_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_es1888, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_es1888, 1);
|
||||
|
@ -117,8 +117,6 @@ static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t
|
||||
static int ess_start(struct ess_chinfo *ch);
|
||||
static int ess_stop(struct ess_chinfo *ch);
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
/*
|
||||
* Common code for the midi and pcm functions
|
||||
*
|
||||
|
@ -120,8 +120,6 @@ static int pnpmss_attach(device_t dev);
|
||||
|
||||
static driver_intr_t opti931_intr;
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
static u_int32_t mss_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
|
@ -105,8 +105,6 @@ static int sb_reset_dsp(struct sb_info *sb);
|
||||
|
||||
static void sb_intr(void *arg);
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
/*
|
||||
* Common code for the midi and pcm functions
|
||||
*
|
||||
|
@ -96,8 +96,6 @@ static int sb_speed(struct sb_chinfo *ch);
|
||||
static int sb_start(struct sb_chinfo *ch);
|
||||
static int sb_stop(struct sb_chinfo *ch);
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
/*
|
||||
* Common code for the midi and pcm functions
|
||||
*
|
||||
|
@ -32,7 +32,7 @@
|
||||
* The ALS4000 is a effectively an SB16 with a PCI interface.
|
||||
*
|
||||
* This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and
|
||||
* SB16 register descriptions.
|
||||
* SB16 register descriptions.
|
||||
*/
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
@ -91,7 +91,7 @@ static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 };
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Register Utilities */
|
||||
|
||||
static u_int32_t
|
||||
static u_int32_t
|
||||
als_gcr_rd(struct sc_info *sc, int index)
|
||||
{
|
||||
bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
|
||||
@ -144,9 +144,9 @@ als_esp_wr(struct sc_info *sc, u_int8_t data)
|
||||
DELAY(20);
|
||||
} while (--tries != 0);
|
||||
|
||||
if (tries == 0)
|
||||
if (tries == 0)
|
||||
device_printf(sc->dev, "als_esp_wr timeout");
|
||||
|
||||
|
||||
bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data);
|
||||
}
|
||||
|
||||
@ -165,15 +165,15 @@ als_esp_reset(struct sc_info *sc)
|
||||
u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8);
|
||||
if (u & 0x80) {
|
||||
v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA);
|
||||
if (v == 0xaa)
|
||||
if (v == 0xaa)
|
||||
return 0;
|
||||
else
|
||||
else
|
||||
break;
|
||||
}
|
||||
DELAY(20);
|
||||
} while (--tries != 0);
|
||||
|
||||
if (tries == 0)
|
||||
if (tries == 0)
|
||||
device_printf(sc->dev, "als_esp_reset timeout");
|
||||
return 1;
|
||||
}
|
||||
@ -189,7 +189,7 @@ als_ack_read(struct sc_info *sc, u_int8_t addr)
|
||||
/* Common pcm channel implementation */
|
||||
|
||||
static void *
|
||||
alschan_init(kobj_t obj, void *devinfo,
|
||||
alschan_init(kobj_t obj, void *devinfo,
|
||||
struct snd_dbuf *b, struct pcm_channel *c, int dir)
|
||||
{
|
||||
struct sc_info *sc = devinfo;
|
||||
@ -216,7 +216,7 @@ alschan_init(kobj_t obj, void *devinfo,
|
||||
}
|
||||
|
||||
static int
|
||||
alschan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
alschan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
|
||||
@ -231,7 +231,7 @@ alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
struct sc_info *sc = ch->parent;
|
||||
|
||||
other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
|
||||
|
||||
|
||||
/* Deny request if other dma channel is active */
|
||||
if (other->dma_active) {
|
||||
ch->speed = other->speed;
|
||||
@ -283,7 +283,7 @@ als_set_speed(struct sc_chinfo *ch)
|
||||
als_esp_wr(sc, ch->speed >> 8);
|
||||
als_esp_wr(sc, ch->speed & 0xff);
|
||||
} else {
|
||||
DEB(printf("speed locked at %d (tried %d)\n",
|
||||
DEB(printf("speed locked at %d (tried %d)\n",
|
||||
other->speed, ch->speed));
|
||||
}
|
||||
}
|
||||
@ -317,7 +317,7 @@ als_get_playback_command(u_int32_t format)
|
||||
return &playback_cmds[i];
|
||||
}
|
||||
}
|
||||
DEB(printf("als_get_playback_command: invalid format 0x%08x\n",
|
||||
DEB(printf("als_get_playback_command: invalid format 0x%08x\n",
|
||||
format));
|
||||
return &playback_cmds[0];
|
||||
}
|
||||
@ -349,7 +349,7 @@ als_playback_start(struct sc_chinfo *ch)
|
||||
als_esp_wr(sc, p->format_val);
|
||||
als_esp_wr(sc, count & 0xff);
|
||||
als_esp_wr(sc, count >> 8);
|
||||
|
||||
|
||||
ch->dma_active = 1;
|
||||
}
|
||||
|
||||
@ -404,13 +404,13 @@ static u_int8_t
|
||||
als_get_fifo_format(struct sc_info *sc, u_int32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case AFMT_U8:
|
||||
case AFMT_U8:
|
||||
return ALS_FIFO1_8BIT;
|
||||
case AFMT_U8 | AFMT_STEREO:
|
||||
case AFMT_U8 | AFMT_STEREO:
|
||||
return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
|
||||
case AFMT_S16_LE:
|
||||
case AFMT_S16_LE:
|
||||
return ALS_FIFO1_SIGNED;
|
||||
case AFMT_S16_LE | AFMT_STEREO:
|
||||
case AFMT_S16_LE | AFMT_STEREO:
|
||||
return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
|
||||
}
|
||||
device_printf(sc->dev, "format not found: 0x%08x\n", format);
|
||||
@ -517,7 +517,7 @@ struct sb16props {
|
||||
};
|
||||
|
||||
static int
|
||||
alsmix_init(struct snd_mixer *m)
|
||||
alsmix_init(struct snd_mixer *m)
|
||||
{
|
||||
u_int32_t i, v;
|
||||
|
||||
@ -528,7 +528,7 @@ alsmix_init(struct snd_mixer *m)
|
||||
|
||||
for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
||||
if (amt[i].iselect) v |= 1 << i;
|
||||
}
|
||||
}
|
||||
mix_setrecdevs(m, v);
|
||||
return 0;
|
||||
}
|
||||
@ -545,7 +545,7 @@ alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
l = (left * mask / 100) & mask;
|
||||
v = als_mix_rd(sc, amt[dev].lreg) & ~mask;
|
||||
als_mix_wr(sc, amt[dev].lreg, l | v);
|
||||
|
||||
|
||||
if (amt[dev].rreg) {
|
||||
r = (right * mask / 100) & mask;
|
||||
v = als_mix_rd(sc, amt[dev].rreg) & ~mask;
|
||||
@ -553,7 +553,7 @@ alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
} else {
|
||||
r = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Zero gain does not mute channel from output, but this does. */
|
||||
v = als_mix_rd(sc, SB16_OMASK);
|
||||
if (l == 0 && r == 0) {
|
||||
@ -599,15 +599,15 @@ als_intr(void *p)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)p;
|
||||
u_int8_t intr, sb_status;
|
||||
|
||||
|
||||
intr = als_intr_rd(sc);
|
||||
|
||||
|
||||
if (intr & 0x80)
|
||||
chn_intr(sc->pch.channel);
|
||||
|
||||
|
||||
if (intr & 0x40)
|
||||
chn_intr(sc->rch.channel);
|
||||
|
||||
|
||||
/* ACK interrupt in PCI core */
|
||||
als_intr_wr(sc, intr);
|
||||
|
||||
@ -623,7 +623,7 @@ als_intr(void *p)
|
||||
if (sb_status & ALS_IRQ_CR1E)
|
||||
als_ack_read(sc, ALS_CR1E_ACK_PORT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* H/W initialization */
|
||||
@ -641,10 +641,10 @@ als_init(struct sc_info *sc)
|
||||
/* Enable write on DMA_SETUP register */
|
||||
v = als_mix_rd(sc, ALS_SB16_CONFIG);
|
||||
als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80);
|
||||
|
||||
|
||||
/* Select DMA0 */
|
||||
als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01);
|
||||
|
||||
|
||||
/* Disable write on DMA_SETUP register */
|
||||
als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f);
|
||||
|
||||
@ -725,7 +725,7 @@ als_resource_grab(device_t dev, struct sc_info *sc)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, als_intr,
|
||||
if (bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, als_intr,
|
||||
sc, &sc->ih)) {
|
||||
device_printf(dev, "unable to setup interrupt\n");
|
||||
goto bad;
|
||||
@ -736,7 +736,7 @@ als_resource_grab(device_t dev, struct sc_info *sc)
|
||||
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
|
||||
/*highaddr*/BUS_SPACE_MAXADDR,
|
||||
/*filter*/NULL, /*filterarg*/NULL,
|
||||
/*maxsize*/ALS_BUFFER_SIZE,
|
||||
/*maxsize*/ALS_BUFFER_SIZE,
|
||||
/*nsegments*/1, /*maxsegz*/0x3ffff,
|
||||
/*flags*/0, &sc->parent_dmat) != 0) {
|
||||
device_printf(dev, "unable to create dma tag\n");
|
||||
@ -854,11 +854,11 @@ static int
|
||||
als_pci_resume(device_t dev)
|
||||
{
|
||||
struct sc_info *sc = pcm_getdevinfo(dev);
|
||||
|
||||
|
||||
if (als_init(sc) != 0) {
|
||||
device_printf(dev, "unable to reinitialize the card\n");
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mixer_reinit(dev) != 0) {
|
||||
device_printf(dev, "unable to reinitialize the mixer\n");
|
||||
@ -891,8 +891,6 @@ static driver_t als_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_als, pci, als_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_als, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_als, 1);
|
||||
|
@ -681,8 +681,6 @@ static driver_t au_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_aureal, pci, au_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_aureal, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_aureal, 1);
|
||||
|
@ -30,7 +30,7 @@
|
||||
* registers and the 8738 mixer devices. His Linux was driver a also
|
||||
* useful reference point.
|
||||
*
|
||||
* TODO: MIDI
|
||||
* TODO: MIDI
|
||||
*
|
||||
* SPDIF contributed by Gerhard Gonter <gonter@whisky.wu-wien.ac.at>.
|
||||
*
|
||||
@ -159,7 +159,7 @@ cmi_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
|
||||
}
|
||||
|
||||
static void
|
||||
cmi_partial_wr4(struct sc_info *sc,
|
||||
cmi_partial_wr4(struct sc_info *sc,
|
||||
int reg, int shift, u_int32_t mask, u_int32_t val)
|
||||
{
|
||||
u_int32_t r;
|
||||
@ -232,9 +232,9 @@ cmpci_regvalue_to_rate(u_int32_t r)
|
||||
* playback or capture. We use ch0 for playback and ch1 for capture. */
|
||||
|
||||
static void
|
||||
cmi_dma_prog(struct sc_info *sc, struct sc_chinfo *ch, u_int32_t base)
|
||||
cmi_dma_prog(struct sc_info *sc, struct sc_chinfo *ch, u_int32_t base)
|
||||
{
|
||||
u_int32_t s, i, sz, physbuf;
|
||||
u_int32_t s, i, sz, physbuf;
|
||||
|
||||
physbuf = vtophys(sndbuf_getbuf(ch->buffer));
|
||||
|
||||
@ -246,7 +246,7 @@ cmi_dma_prog(struct sc_info *sc, struct sc_chinfo *ch, u_int32_t base)
|
||||
|
||||
i = sz / (ch->bps * CMI_INTR_PER_BUFFER) - 1;
|
||||
cmi_wr(sc, base + 6, i, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
@ -254,8 +254,8 @@ cmi_ch0_start(struct sc_info *sc, struct sc_chinfo *ch)
|
||||
{
|
||||
cmi_dma_prog(sc, ch, CMPCI_REG_DMA0_BASE);
|
||||
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_INTR_CTRL,
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_INTR_CTRL,
|
||||
CMPCI_REG_CH0_INTR_ENABLE);
|
||||
|
||||
ch->dma_active = 1;
|
||||
@ -267,8 +267,8 @@ cmi_ch0_stop(struct sc_info *sc, struct sc_chinfo *ch)
|
||||
u_int32_t r = ch->dma_active;
|
||||
|
||||
cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_RESET);
|
||||
ch->dma_active = 0;
|
||||
return r;
|
||||
@ -278,9 +278,9 @@ static void
|
||||
cmi_ch1_start(struct sc_info *sc, struct sc_chinfo *ch)
|
||||
{
|
||||
cmi_dma_prog(sc, ch, CMPCI_REG_DMA1_BASE);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
|
||||
/* Enable Interrupts */
|
||||
cmi_set4(sc, CMPCI_REG_INTR_CTRL,
|
||||
cmi_set4(sc, CMPCI_REG_INTR_CTRL,
|
||||
CMPCI_REG_CH1_INTR_ENABLE);
|
||||
DEB(printf("cmi_ch1_start: dma prog\n"));
|
||||
ch->dma_active = 1;
|
||||
@ -292,8 +292,8 @@ cmi_ch1_stop(struct sc_info *sc, struct sc_chinfo *ch)
|
||||
u_int32_t r = ch->dma_active;
|
||||
|
||||
cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_RESET);
|
||||
ch->dma_active = 0;
|
||||
return r;
|
||||
@ -324,7 +324,7 @@ cmi_spdif_speed(struct sc_info *sc, int speed) {
|
||||
/* Channel Interface implementation */
|
||||
|
||||
static void *
|
||||
cmichan_init(kobj_t obj, void *devinfo,
|
||||
cmichan_init(kobj_t obj, void *devinfo,
|
||||
struct snd_dbuf *b, struct pcm_channel *c, int dir)
|
||||
{
|
||||
struct sc_info *sc = devinfo;
|
||||
@ -740,18 +740,18 @@ cmi_init(struct sc_info *sc)
|
||||
CMPCI_REG_CH0_INTR_ENABLE | CMPCI_REG_CH1_INTR_ENABLE);
|
||||
|
||||
/* Configure DMA channels, ch0 = play, ch1 = capture */
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_DIR);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_DIR);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_DIR);
|
||||
cmi_set4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_DIR);
|
||||
|
||||
/* Attempt to enable 4 Channel output */
|
||||
cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_N4SPK3D);
|
||||
cmi_set4(sc, CMPCI_REG_MISC, CMPCI_REG_N4SPK3D);
|
||||
|
||||
/* Disable SPDIF1 - not compatible with config */
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIF1_ENABLE);
|
||||
cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_SPDIF_LOOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmi_uninit(struct sc_info *sc)
|
||||
@ -860,15 +860,15 @@ cmi_attach(device_t dev)
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
if (sc->parent_dmat)
|
||||
if (sc->parent_dmat)
|
||||
bus_dma_tag_destroy(sc->parent_dmat);
|
||||
if (sc->ih)
|
||||
if (sc->ih)
|
||||
bus_teardown_intr(dev, sc->irq, sc->ih);
|
||||
if (sc->irq)
|
||||
if (sc->irq)
|
||||
bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
|
||||
if (sc->reg)
|
||||
if (sc->reg)
|
||||
bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
|
||||
if (sc)
|
||||
if (sc)
|
||||
free(sc, M_DEVBUF);
|
||||
|
||||
return ENXIO;
|
||||
@ -921,7 +921,7 @@ cmi_resume(device_t dev)
|
||||
if (mixer_reinit(dev) == -1) {
|
||||
device_printf(dev, "unable to reinitialize the mixer\n");
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->pch.dma_was_active) {
|
||||
cmichan_setspeed(NULL, &sc->pch, sc->pch.spd);
|
||||
@ -952,7 +952,6 @@ static driver_t cmi_driver = {
|
||||
sizeof(struct snddev_info)
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
DRIVER_MODULE(snd_cmipci, pci, cmi_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_cmipci, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_cmipci, 1);
|
||||
|
@ -780,7 +780,7 @@ cs4281_pci_attach(device_t dev)
|
||||
if (data & CS4281PCI_PMCS_PS_MASK) {
|
||||
/* Reset the power state. */
|
||||
device_printf(dev, "chip is in D%d power mode "
|
||||
"-- setting to D0\n",
|
||||
"-- setting to D0\n",
|
||||
data & CS4281PCI_PMCS_PS_MASK);
|
||||
pci_write_config(dev, CS4281PCI_PMCS_OFFSET,
|
||||
data & ~CS4281PCI_PMCS_PS_MASK, 4);
|
||||
@ -974,8 +974,6 @@ static driver_t cs4281_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_cs4281, pci, cs4281_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_cs4281, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_cs4281, 1);
|
||||
|
@ -841,8 +841,6 @@ static driver_t pcmcsa_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_csapcm, csa, pcmcsa_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_csapcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1);
|
||||
|
@ -1071,8 +1071,6 @@ static driver_t ds1_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_ds1, pci, ds1_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_ds1, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_ds1, 1);
|
||||
|
@ -1547,8 +1547,6 @@ static driver_t emu_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_emu10k1, pci, emu_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_emu10k1, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_emu10k1, 1);
|
||||
|
@ -954,8 +954,6 @@ static driver_t es_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_es137x, pci, es_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_es137x, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_es137x, 1);
|
||||
|
@ -704,6 +704,4 @@ static driver_t fm801_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0);
|
||||
|
@ -1185,8 +1185,6 @@ static driver_t agg_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_maestro, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_maestro, 1);
|
||||
|
@ -1495,8 +1495,6 @@ static driver_t m3_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_maestro3, pci, m3_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_maestro3, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_maestro3, 1);
|
||||
|
@ -591,7 +591,7 @@ nm_pci_probe(device_t dev)
|
||||
return ENXIO;
|
||||
}
|
||||
|
||||
if ((nm_rd(sc, NM_MIXER_PRESENCE, 2) &
|
||||
if ((nm_rd(sc, NM_MIXER_PRESENCE, 2) &
|
||||
NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
|
||||
i = 0; /* non-ac97 card, but not listed */
|
||||
DEB(device_printf(dev, "subdev = 0x%x - badcard?\n",
|
||||
@ -746,8 +746,6 @@ static driver_t nm_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_neomagic, pci, nm_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_neomagic, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_neomagic, 1);
|
||||
|
@ -115,8 +115,6 @@ static int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t c
|
||||
static int ess_dmapos(struct ess_info *sc, int ch);
|
||||
static int ess_dmatrigger(struct ess_info *sc, int ch, int go);
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
/*
|
||||
* Common code for the midi and pcm functions
|
||||
*
|
||||
|
@ -829,8 +829,6 @@ static driver_t tr_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_t4dwave, 1);
|
||||
|
@ -581,8 +581,6 @@ static driver_t via_driver = {
|
||||
sizeof(struct snddev_info),
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
|
||||
DRIVER_MODULE(via, pci, via_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(via, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(via, 1);
|
||||
|
@ -923,7 +923,6 @@ static driver_t sonicvibes_driver = {
|
||||
sizeof(struct snddev_info)
|
||||
};
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
DRIVER_MODULE(snd_sonicvibes, pci, sonicvibes_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(snd_sonicvibes, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
|
||||
MODULE_VERSION(snd_sonicvibes, 1);
|
||||
|
@ -510,7 +510,7 @@ chn_poll(struct pcm_channel *c, int ev, struct proc *p)
|
||||
int
|
||||
chn_abort(struct pcm_channel *c)
|
||||
{
|
||||
int missing = 0, cnt = 0;
|
||||
int missing = 0;
|
||||
struct snd_dbuf *b = c->bufhard;
|
||||
struct snd_dbuf *bs = c->bufsoft;
|
||||
|
||||
@ -519,15 +519,6 @@ chn_abort(struct pcm_channel *c)
|
||||
return 0;
|
||||
c->flags |= CHN_F_ABORTING;
|
||||
|
||||
/*
|
||||
* wait up to 200ms for the secondary buffer to empty-
|
||||
* a vchan will never have data in the secondary buffer so we won't sleep
|
||||
*/
|
||||
cnt = 10;
|
||||
while ((sndbuf_getready(bs) > 0) && (cnt-- > 0)) {
|
||||
chn_sleep(c, "pcmabr", hz / 50);
|
||||
}
|
||||
|
||||
c->flags &= ~CHN_F_TRIGGERED;
|
||||
/* kill the channel */
|
||||
chn_trigger(c, PCMTRIG_ABORT);
|
||||
|
@ -33,36 +33,117 @@
|
||||
|
||||
#define OLDPCM_IOCTL
|
||||
|
||||
static int
|
||||
getchns(struct snddev_info *d, int chan, struct pcm_channel **rdch, struct pcm_channel **wrch)
|
||||
static d_open_t dsp_open;
|
||||
static d_close_t dsp_close;
|
||||
static d_read_t dsp_read;
|
||||
static d_write_t dsp_write;
|
||||
static d_ioctl_t dsp_ioctl;
|
||||
static d_poll_t dsp_poll;
|
||||
static d_mmap_t dsp_mmap;
|
||||
|
||||
static struct cdevsw dsp_cdevsw = {
|
||||
/* open */ dsp_open,
|
||||
/* close */ dsp_close,
|
||||
/* read */ dsp_read,
|
||||
/* write */ dsp_write,
|
||||
/* ioctl */ dsp_ioctl,
|
||||
/* poll */ dsp_poll,
|
||||
/* mmap */ dsp_mmap,
|
||||
/* strategy */ nostrategy,
|
||||
/* name */ "dsp",
|
||||
/* maj */ SND_CDEV_MAJOR,
|
||||
/* dump */ nodump,
|
||||
/* psize */ nopsize,
|
||||
/* flags */ 0,
|
||||
};
|
||||
|
||||
static eventhandler_tag dsp_ehtag;
|
||||
|
||||
static struct snddev_info *
|
||||
dsp_get_info(dev_t dev)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int unit;
|
||||
|
||||
unit = PCMUNIT(dev);
|
||||
if (unit > devclass_get_maxunit(pcm_devclass))
|
||||
return NULL;
|
||||
d = devclass_get_softc(pcm_devclass, unit);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the channels channels associated with an open device instance.
|
||||
* set the priority if the device is simplex and one direction (only) is
|
||||
* specified.
|
||||
* lock channels specified.
|
||||
*/
|
||||
static int
|
||||
getchns(dev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
|
||||
d = dsp_get_info(dev);
|
||||
snd_mtxlock(d->lock);
|
||||
d->inprog++;
|
||||
KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
|
||||
("getchns: read and write both prioritised"));
|
||||
|
||||
if ((d->flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR)))
|
||||
d->flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
|
||||
|
||||
*rdch = dev->si_drv1;
|
||||
*wrch = dev->si_drv2;
|
||||
if ((d->flags & SD_F_SIMPLEX) && (d->flags & SD_F_PRIO_SET)) {
|
||||
*rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : d->fakechan;
|
||||
*wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : d->fakechan;
|
||||
if (prio) {
|
||||
if (*rdch && d->flags & SD_F_PRIO_WR) {
|
||||
dev->si_drv1 = NULL;
|
||||
*rdch = d->fakechan;
|
||||
} else if (*wrch && d->flags & SD_F_PRIO_RD) {
|
||||
dev->si_drv2 = NULL;
|
||||
*wrch = d->fakechan;
|
||||
}
|
||||
}
|
||||
|
||||
d->fakechan->flags |= CHN_F_BUSY;
|
||||
} else {
|
||||
*rdch = d->arec[chan];
|
||||
*wrch = d->aplay[chan];
|
||||
}
|
||||
|
||||
if (*rdch && *rdch != d->fakechan && (prio & SD_F_PRIO_RD))
|
||||
CHN_LOCK(*rdch);
|
||||
if (*wrch && *wrch != d->fakechan && (prio & SD_F_PRIO_WR))
|
||||
CHN_LOCK(*wrch);
|
||||
snd_mtxunlock(d->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unlock specified channels */
|
||||
static void
|
||||
setchns(struct snddev_info *d, int chan)
|
||||
relchns(dev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
|
||||
{
|
||||
KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
|
||||
("getchns: read and write both prioritised"));
|
||||
d->flags |= SD_F_DIR_SET;
|
||||
struct snddev_info *d;
|
||||
|
||||
d = dsp_get_info(dev);
|
||||
if (wrch && wrch != d->fakechan && (prio & SD_F_PRIO_WR))
|
||||
CHN_UNLOCK(wrch);
|
||||
if (rdch && rdch != d->fakechan && (prio & SD_F_PRIO_RD))
|
||||
CHN_UNLOCK(rdch);
|
||||
snd_mtxlock(d->lock);
|
||||
d->inprog--;
|
||||
snd_mtxunlock(d->lock);
|
||||
}
|
||||
|
||||
int
|
||||
dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
static int
|
||||
dsp_open(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
struct pcm_channel *rdch, *wrch;
|
||||
struct snddev_info *d;
|
||||
u_int32_t fmt;
|
||||
int devtype;
|
||||
|
||||
d = dsp_get_info(i_dev);
|
||||
devtype = PCMDEV(i_dev);
|
||||
|
||||
/* decide default format */
|
||||
switch (devtype) {
|
||||
@ -88,26 +169,21 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
|
||||
/* lock snddev so nobody else can monkey with it */
|
||||
snd_mtxlock(d->lock);
|
||||
if (chan >= d->chancount) {
|
||||
/* not a valid channel, exit */
|
||||
snd_mtxunlock(d->lock);
|
||||
return ENODEV;
|
||||
}
|
||||
if ((d->flags & SD_F_SIMPLEX) && (d->arec[chan] || d->aplay[chan])) {
|
||||
if ((d->flags & SD_F_SIMPLEX) && (i_dev->si_drv1 || i_dev->si_drv2)) {
|
||||
/* simplex device, already open, exit */
|
||||
snd_mtxunlock(d->lock);
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
/* if we get here, the open request is valid */
|
||||
rdch = d->arec[chan];
|
||||
wrch = d->aplay[chan];
|
||||
rdch = i_dev->si_drv1;
|
||||
wrch = i_dev->si_drv2;
|
||||
|
||||
if (oflags & FREAD) {
|
||||
if (flags & FREAD) {
|
||||
/* open for read */
|
||||
if (rdch == NULL) {
|
||||
/* not already open, try to get a channel */
|
||||
rdch = pcm_chnalloc(d, PCMDIR_REC, pid);
|
||||
rdch = pcm_chnalloc(d, PCMDIR_REC, p->p_pid);
|
||||
if (!rdch) {
|
||||
/* no channel available, exit */
|
||||
snd_mtxunlock(d->lock);
|
||||
@ -121,14 +197,14 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
}
|
||||
}
|
||||
|
||||
if (oflags & FWRITE) {
|
||||
if (flags & FWRITE) {
|
||||
/* open for write */
|
||||
if (wrch == NULL) {
|
||||
/* not already open, try to get a channel */
|
||||
wrch = pcm_chnalloc(d, PCMDIR_PLAY, pid);
|
||||
wrch = pcm_chnalloc(d, PCMDIR_PLAY, p->p_pid);
|
||||
if (!wrch) {
|
||||
/* no channel available */
|
||||
if (rdch && (oflags & FREAD)) {
|
||||
if (rdch && (flags & FREAD)) {
|
||||
/* just opened a read channel, release it */
|
||||
pcm_chnrelease(rdch);
|
||||
}
|
||||
@ -139,7 +215,7 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
/* got a channel, already locked for us */
|
||||
} else {
|
||||
/* already open for write */
|
||||
if (rdch && (oflags & FREAD)) {
|
||||
if (rdch && (flags & FREAD)) {
|
||||
/* just opened a read channel, release it */
|
||||
pcm_chnrelease(rdch);
|
||||
}
|
||||
@ -149,16 +225,16 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
}
|
||||
}
|
||||
|
||||
d->aplay[chan] = wrch;
|
||||
d->arec[chan] = rdch;
|
||||
i_dev->si_drv1 = rdch;
|
||||
i_dev->si_drv2 = wrch;
|
||||
snd_mtxunlock(d->lock);
|
||||
/* finished with snddev, new channels still locked */
|
||||
|
||||
/* bump refcounts, reset and unlock any channels that we just opened */
|
||||
if (rdch) {
|
||||
if (oflags & FREAD) {
|
||||
if (flags & FREAD) {
|
||||
chn_reset(rdch, fmt);
|
||||
if (oflags & O_NONBLOCK)
|
||||
if (flags & O_NONBLOCK)
|
||||
rdch->flags |= CHN_F_NBIO;
|
||||
} else {
|
||||
CHN_LOCK(rdch);
|
||||
@ -167,9 +243,9 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
if (wrch) {
|
||||
if (oflags & FWRITE) {
|
||||
if (flags & FWRITE) {
|
||||
chn_reset(wrch, fmt);
|
||||
if (oflags & O_NONBLOCK)
|
||||
if (flags & O_NONBLOCK)
|
||||
wrch->flags |= CHN_F_NBIO;
|
||||
} else {
|
||||
CHN_LOCK(wrch);
|
||||
@ -180,15 +256,17 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_close(struct snddev_info *d, int chan, int devtype)
|
||||
static int
|
||||
dsp_close(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
struct pcm_channel *rdch, *wrch;
|
||||
struct snddev_info *d;
|
||||
int exit;
|
||||
|
||||
d = dsp_get_info(i_dev);
|
||||
snd_mtxlock(d->lock);
|
||||
rdch = d->arec[chan];
|
||||
wrch = d->aplay[chan];
|
||||
rdch = i_dev->si_drv1;
|
||||
wrch = i_dev->si_drv2;
|
||||
|
||||
exit = 0;
|
||||
|
||||
@ -217,20 +295,20 @@ dsp_close(struct snddev_info *d, int chan, int devtype)
|
||||
if (d->fakechan)
|
||||
d->fakechan->flags = 0;
|
||||
|
||||
d->aplay[chan] = NULL;
|
||||
d->arec[chan] = NULL;
|
||||
i_dev->si_drv1 = NULL;
|
||||
i_dev->si_drv2 = NULL;
|
||||
|
||||
d->flags &= ~SD_F_TRANSIENT;
|
||||
snd_mtxunlock(d->lock);
|
||||
|
||||
if (rdch) {
|
||||
chn_abort(rdch);
|
||||
chn_abort(rdch); /* won't sleep */
|
||||
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
|
||||
chn_reset(rdch, 0);
|
||||
pcm_chnrelease(rdch);
|
||||
}
|
||||
if (wrch) {
|
||||
chn_flush(wrch);
|
||||
chn_flush(wrch); /* may sleep */
|
||||
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
|
||||
chn_reset(wrch, 0);
|
||||
pcm_chnrelease(wrch);
|
||||
@ -239,86 +317,89 @@ dsp_close(struct snddev_info *d, int chan, int devtype)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_read(struct snddev_info *d, int chan, struct uio *buf, int flag)
|
||||
static int
|
||||
dsp_read(dev_t i_dev, struct uio *buf, int flag)
|
||||
{
|
||||
struct pcm_channel *rdch, *wrch;
|
||||
int ret;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
|
||||
if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
|
||||
|
||||
getchns(d, chan, &rdch, &wrch);
|
||||
CHN_LOCK(rdch);
|
||||
snd_mtxunlock(d->lock);
|
||||
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
|
||||
|
||||
KASSERT(rdch, ("dsp_read: nonexistant channel"));
|
||||
KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
|
||||
|
||||
if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
|
||||
CHN_UNLOCK(rdch);
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
|
||||
return EINVAL;
|
||||
}
|
||||
if (!(rdch->flags & CHN_F_RUNNING))
|
||||
rdch->flags |= CHN_F_RUNNING;
|
||||
ret = chn_read(rdch, buf);
|
||||
CHN_UNLOCK(rdch);
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_write(struct snddev_info *d, int chan, struct uio *buf, int flag)
|
||||
static int
|
||||
dsp_write(dev_t i_dev, struct uio *buf, int flag)
|
||||
{
|
||||
struct pcm_channel *rdch, *wrch;
|
||||
int ret;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
|
||||
if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
|
||||
|
||||
getchns(d, chan, &rdch, &wrch);
|
||||
CHN_LOCK(wrch);
|
||||
snd_mtxunlock(d->lock);
|
||||
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
|
||||
|
||||
KASSERT(wrch, ("dsp_write: nonexistant channel"));
|
||||
KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
|
||||
|
||||
if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
|
||||
CHN_UNLOCK(wrch);
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
|
||||
return EINVAL;
|
||||
}
|
||||
if (!(wrch->flags & CHN_F_RUNNING))
|
||||
wrch->flags |= CHN_F_RUNNING;
|
||||
ret = chn_write(wrch, buf);
|
||||
CHN_UNLOCK(wrch);
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
static int
|
||||
dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
|
||||
{
|
||||
int ret = 0, *arg_i = (int *)arg;
|
||||
u_long s;
|
||||
struct pcm_channel *wrch, *rdch;
|
||||
struct snddev_info *d;
|
||||
int kill;
|
||||
int ret = 0, *arg_i = (int *)arg, tmp;
|
||||
u_long s;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
rdch = d->arec[chan];
|
||||
wrch = d->aplay[chan];
|
||||
if (rdch)
|
||||
CHN_LOCK(rdch);
|
||||
if (wrch)
|
||||
CHN_LOCK(wrch);
|
||||
snd_mtxunlock(d->lock);
|
||||
/*
|
||||
* this is an evil hack to allow broken apps to perform mixer ioctls
|
||||
* on dsp devices.
|
||||
*/
|
||||
|
||||
if (rdch && (rdch->flags & CHN_F_DEAD))
|
||||
rdch = NULL;
|
||||
if (IOCGROUP(cmd) == 'M') {
|
||||
dev_t pdev;
|
||||
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
|
||||
return mixer_ioctl(pdev, cmd, arg, mode, p);
|
||||
}
|
||||
|
||||
d = dsp_get_info(i_dev);
|
||||
getchns(i_dev, &rdch, &wrch, 0);
|
||||
|
||||
kill = 0;
|
||||
if (wrch && (wrch->flags & CHN_F_DEAD))
|
||||
wrch = NULL;
|
||||
if (!(rdch || wrch))
|
||||
kill |= 1;
|
||||
if (rdch && (rdch->flags & CHN_F_DEAD))
|
||||
kill |= 2;
|
||||
if (kill == 3) {
|
||||
relchns(i_dev, rdch, wrch, 0);
|
||||
return EINVAL;
|
||||
}
|
||||
if (kill & 1)
|
||||
wrch = NULL;
|
||||
if (kill & 2)
|
||||
rdch = NULL;
|
||||
|
||||
/*
|
||||
* all routines are called with int. blocked. Make sure that
|
||||
@ -342,12 +423,22 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
{
|
||||
struct snd_size *p = (struct snd_size *)arg;
|
||||
|
||||
if (wrch)
|
||||
p->play_size = 0;
|
||||
p->rec_size = 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_setblocksize(wrch, 2, p->play_size);
|
||||
if (rdch)
|
||||
p->play_size = sndbuf_getblksz(wrch->bufsoft);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
chn_setblocksize(rdch, 2, p->rec_size);
|
||||
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
break;
|
||||
case AIOGSIZE: /* get the current blocksize */
|
||||
{
|
||||
struct snd_size *p = (struct snd_size *)arg;
|
||||
@ -364,12 +455,16 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
snd_chan_param *p = (snd_chan_param *)arg;
|
||||
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_setformat(wrch, p->play_format);
|
||||
chn_setspeed(wrch, p->play_rate);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
chn_setformat(rdch, p->rec_format);
|
||||
chn_setspeed(rdch, p->rec_rate);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
@ -389,11 +484,16 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
{
|
||||
snd_capabilities *p = (snd_capabilities *)arg;
|
||||
struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
|
||||
dev_t pdev;
|
||||
|
||||
if (rdch)
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
rcaps = chn_getcaps(rdch);
|
||||
if (wrch)
|
||||
}
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
pcaps = chn_getcaps(wrch);
|
||||
}
|
||||
p->rate_min = max(rcaps? rcaps->minspeed : 0,
|
||||
pcaps? pcaps->minspeed : 0);
|
||||
p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
|
||||
@ -405,9 +505,14 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
(wrch? chn_getformats(wrch) : 0xffffffff);
|
||||
if (rdch && wrch)
|
||||
p->formats |= (d->flags & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(device_get_unit(d->dev), SND_DEV_CTL, 0));
|
||||
p->mixers = 1; /* default: one mixer */
|
||||
p->inputs = mix_getdevs(d->mixer);
|
||||
p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
|
||||
p->left = p->right = 100;
|
||||
if (wrch)
|
||||
CHN_UNLOCK(wrch);
|
||||
if (rdch)
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -470,10 +575,16 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
|
||||
case SNDCTL_DSP_SETBLKSIZE:
|
||||
RANGE(*arg_i, 16, 65536);
|
||||
if (wrch)
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_setblocksize(wrch, 2, *arg_i);
|
||||
if (rdch)
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
chn_setblocksize(rdch, 2, *arg_i);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_RESET:
|
||||
@ -486,40 +597,75 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
|
||||
case SNDCTL_DSP_SYNC:
|
||||
DEB(printf("dsp sync\n"));
|
||||
if (wrch) chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
|
||||
/* chn_sync may sleep */
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_SPEED:
|
||||
if (wrch)
|
||||
/* chn_setspeed may sleep */
|
||||
tmp = 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setspeed(wrch, *arg_i);
|
||||
if (rdch && ret == 0)
|
||||
tmp = wrch->speed;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setspeed(rdch, *arg_i);
|
||||
/* fallthru */
|
||||
if (tmp == 0)
|
||||
tmp = rdch->speed;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
break;
|
||||
|
||||
case SOUND_PCM_READ_RATE:
|
||||
*arg_i = wrch? wrch->speed : rdch->speed;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_STEREO:
|
||||
if (wrch)
|
||||
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
|
||||
((*arg_i)? AFMT_STEREO : 0));
|
||||
if (rdch && ret == 0)
|
||||
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
|
||||
((*arg_i)? AFMT_STEREO : 0));
|
||||
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
|
||||
tmp = -1;
|
||||
*arg_i = (*arg_i)? AFMT_STEREO : 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
|
||||
tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
|
||||
if (tmp == -1)
|
||||
tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
break;
|
||||
|
||||
case SOUND_PCM_WRITE_CHANNELS:
|
||||
/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
|
||||
if (*arg_i == 1 || *arg_i == 2) {
|
||||
if (wrch)
|
||||
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
|
||||
((*arg_i == 2)? AFMT_STEREO : 0));
|
||||
if (rdch && ret == 0)
|
||||
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
|
||||
((*arg_i == 2)? AFMT_STEREO : 0));
|
||||
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
|
||||
tmp = 0;
|
||||
*arg_i = (*arg_i == 2)? AFMT_STEREO : 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
|
||||
tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
|
||||
if (tmp == 0)
|
||||
tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
} else
|
||||
*arg_i = 0;
|
||||
break;
|
||||
@ -533,19 +679,31 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
break ;
|
||||
|
||||
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
|
||||
/* XXX locking */
|
||||
if ((*arg_i != AFMT_QUERY)) {
|
||||
if (wrch)
|
||||
tmp = 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
|
||||
if (rdch && ret == 0)
|
||||
tmp = wrch->format & ~AFMT_STEREO;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
|
||||
}
|
||||
*arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
|
||||
if (tmp == 0)
|
||||
tmp = rdch->format & ~AFMT_STEREO;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
} else
|
||||
*arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_SETFRAGMENT:
|
||||
/* XXX locking */
|
||||
DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
|
||||
{
|
||||
struct pcm_channel *c = wrch? wrch : rdch;
|
||||
u_int32_t fragln = (*arg_i) & 0x0000ffff;
|
||||
u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
|
||||
u_int32_t fragsz;
|
||||
@ -563,18 +721,27 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
|
||||
|
||||
DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
|
||||
if (rdch)
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setblocksize(rdch, maxfrags, fragsz);
|
||||
if (wrch && ret == 0)
|
||||
maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
|
||||
fragsz = sndbuf_getblksz(rdch->bufsoft);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
if (wrch && ret == 0) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setblocksize(wrch, maxfrags, fragsz);
|
||||
maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
|
||||
fragsz = sndbuf_getblksz(wrch->bufsoft);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
|
||||
fragsz = sndbuf_getblksz(c->bufsoft);
|
||||
fragln = 0;
|
||||
while (fragsz > 1) {
|
||||
fragln++;
|
||||
fragsz >>= 1;
|
||||
}
|
||||
*arg_i = (sndbuf_getblkcnt(c->bufsoft) << 16) | fragln;
|
||||
*arg_i = (maxfrags << 16) | fragln;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -585,11 +752,13 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
if (rdch) {
|
||||
struct snd_dbuf *bs = rdch->bufsoft;
|
||||
|
||||
CHN_LOCK(rdch);
|
||||
chn_rdupdate(rdch);
|
||||
a->bytes = sndbuf_getfree(bs);
|
||||
a->fragments = a->bytes / sndbuf_getblksz(bs);
|
||||
a->fragstotal = sndbuf_getblkcnt(bs);
|
||||
a->fragsize = sndbuf_getblksz(bs);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -601,11 +770,13 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
if (wrch) {
|
||||
struct snd_dbuf *bs = wrch->bufsoft;
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_wrupdate(wrch);
|
||||
a->bytes = sndbuf_getfree(bs);
|
||||
a->fragments = a->bytes / sndbuf_getblksz(bs);
|
||||
a->fragstotal = sndbuf_getblkcnt(bs);
|
||||
a->fragsize = sndbuf_getblksz(bs);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -616,11 +787,13 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
if (rdch) {
|
||||
struct snd_dbuf *bs = rdch->bufsoft;
|
||||
|
||||
CHN_LOCK(rdch);
|
||||
chn_rdupdate(rdch);
|
||||
a->bytes = sndbuf_gettotal(bs);
|
||||
a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
|
||||
a->ptr = sndbuf_getreadyptr(bs);
|
||||
rdch->blocks = sndbuf_getblocks(bs);
|
||||
CHN_UNLOCK(rdch);
|
||||
} else
|
||||
ret = EINVAL;
|
||||
}
|
||||
@ -632,11 +805,13 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
if (wrch) {
|
||||
struct snd_dbuf *bs = wrch->bufsoft;
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_wrupdate(wrch);
|
||||
a->bytes = sndbuf_gettotal(bs);
|
||||
a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
|
||||
a->ptr = sndbuf_getreadyptr(bs);
|
||||
wrch->blocks = sndbuf_getblocks(bs);
|
||||
CHN_UNLOCK(wrch);
|
||||
} else
|
||||
ret = EINVAL;
|
||||
}
|
||||
@ -654,20 +829,24 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
|
||||
case SNDCTL_DSP_SETTRIGGER:
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
|
||||
if (*arg_i & PCM_ENABLE_INPUT) {
|
||||
rdch->flags |= CHN_F_TRIGGERED;
|
||||
chn_start(rdch, 1);
|
||||
} else
|
||||
rdch->flags |= CHN_F_NOTRIGGER;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
|
||||
if (*arg_i & PCM_ENABLE_OUTPUT) {
|
||||
wrch->flags |= CHN_F_TRIGGERED;
|
||||
chn_start(wrch, 1);
|
||||
} else
|
||||
wrch->flags |= CHN_F_NOTRIGGER;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -684,16 +863,20 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
struct snd_dbuf *b = wrch->bufhard;
|
||||
struct snd_dbuf *bs = wrch->bufsoft;
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_wrupdate(wrch);
|
||||
*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
|
||||
CHN_UNLOCK(wrch);
|
||||
} else
|
||||
ret = EINVAL;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_POST:
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
wrch->flags &= ~CHN_F_NOTRIGGER;
|
||||
chn_start(wrch, 1);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -711,72 +894,161 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg)
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (wrch)
|
||||
CHN_UNLOCK(wrch);
|
||||
if (rdch)
|
||||
CHN_UNLOCK(rdch);
|
||||
relchns(i_dev, rdch, wrch, 0);
|
||||
splx(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_poll(struct snddev_info *d, int chan, int events, struct proc *p)
|
||||
static int
|
||||
dsp_poll(dev_t i_dev, int events, struct proc *p)
|
||||
{
|
||||
int ret, e;
|
||||
struct pcm_channel *wrch = NULL, *rdch = NULL;
|
||||
|
||||
ret = 0;
|
||||
snd_mtxlock(d->lock);
|
||||
getchns(d, chan, &rdch, &wrch);
|
||||
if (rdch)
|
||||
CHN_LOCK(rdch);
|
||||
if (wrch)
|
||||
CHN_LOCK(wrch);
|
||||
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
||||
|
||||
if (wrch) {
|
||||
e = (events & (POLLOUT | POLLWRNORM));
|
||||
if (e)
|
||||
ret |= chn_poll(wrch, e, p);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
e = (events & (POLLIN | POLLRDNORM));
|
||||
if (e)
|
||||
ret |= chn_poll(rdch, e, p);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
snd_mtxunlock(d->lock);
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
dsp_mmap(dev_t i_dev, vm_offset_t offset, int nprot)
|
||||
{
|
||||
struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
|
||||
int ret;
|
||||
|
||||
if (nprot & PROT_EXEC)
|
||||
return -1;
|
||||
|
||||
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
||||
#if 0
|
||||
/*
|
||||
* XXX the linux api uses the nprot to select read/write buffer
|
||||
* our vm system doesn't allow this, so force write buffer
|
||||
*/
|
||||
|
||||
if (wrch && (nprot & PROT_WRITE))
|
||||
c = wrch;
|
||||
else if (rdch && (nprot & PROT_READ))
|
||||
c = rdch;
|
||||
else
|
||||
return -1;
|
||||
#else
|
||||
c = wrch;
|
||||
#endif
|
||||
|
||||
if (c == NULL) {
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->flags |= CHN_F_MAPPED;
|
||||
ret = atop(vtophys(((char *)sndbuf_getbuf(c->bufsoft)) + offset));
|
||||
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_mmap(struct snddev_info *d, int chan, vm_offset_t offset, int nprot)
|
||||
dsp_register(int unit, int channel)
|
||||
{
|
||||
struct pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
|
||||
int ret;
|
||||
make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
|
||||
make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
|
||||
make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
|
||||
|
||||
getchns(d, chan, &rdch, &wrch);
|
||||
#if 0
|
||||
/*
|
||||
* XXX the linux api uses the nprot to select read/write bufhard
|
||||
* our vm system doesn't allow this, so force write bufhard
|
||||
*/
|
||||
|
||||
if (1 || (wrch && (nprot & PROT_WRITE)))
|
||||
c = wrch;
|
||||
else if (rdch && (nprot & PROT_READ))
|
||||
c = rdch;
|
||||
#else
|
||||
c = wrch;
|
||||
#endif
|
||||
|
||||
if (c == NULL)
|
||||
return -1;
|
||||
CHN_LOCK(c);
|
||||
c->flags |= CHN_F_MAPPED;
|
||||
ret = atop(vtophys(((char *)sndbuf_getbuf(c->bufsoft)) + offset));
|
||||
CHN_UNLOCK(c);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_unregister(int unit, int channel)
|
||||
{
|
||||
dev_t pdev;
|
||||
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, channel));
|
||||
destroy_dev(pdev);
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, channel));
|
||||
destroy_dev(pdev);
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, channel));
|
||||
destroy_dev(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dsp_clone(void *arg, char *name, int namelen, dev_t *dev)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
dev_t pdev;
|
||||
int unit, devtype;
|
||||
|
||||
if (*dev != NODEV)
|
||||
return;
|
||||
unit = -1;
|
||||
|
||||
devtype = SND_DEV_DSP;
|
||||
if (strcmp(name, "dsp") == 0) {
|
||||
unit = snd_unit;
|
||||
goto gotit;
|
||||
} else if (dev_stdclone(name, NULL, "dsp", &unit) == 1)
|
||||
goto gotit;
|
||||
|
||||
devtype = SND_DEV_DSP16;
|
||||
if (strcmp(name, "dspW") == 0) {
|
||||
unit = snd_unit;
|
||||
goto gotit;
|
||||
} else if (dev_stdclone(name, NULL, "dspW", &unit) == 1)
|
||||
goto gotit;
|
||||
|
||||
devtype = SND_DEV_AUDIO;
|
||||
if (strcmp(name, "audio") == 0) {
|
||||
unit = snd_unit;
|
||||
goto gotit;
|
||||
} else if (dev_stdclone(name, NULL, "audio", &unit) == 1)
|
||||
goto gotit;
|
||||
|
||||
return;
|
||||
gotit:
|
||||
if (unit == -1 || unit > devclass_get_maxunit(pcm_devclass))
|
||||
return;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, unit);
|
||||
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, devtype, d->defaultchan++));
|
||||
if (d->defaultchan >= d->chancount)
|
||||
d->defaultchan = 0;
|
||||
if (pdev->si_flags & SI_NAMED)
|
||||
*dev = pdev;
|
||||
}
|
||||
|
||||
static void
|
||||
dsp_sysinit(void *p)
|
||||
{
|
||||
dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
|
||||
}
|
||||
|
||||
static void
|
||||
dsp_sysuninit(void *p)
|
||||
{
|
||||
if (dsp_ehtag != NULL)
|
||||
EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
|
||||
}
|
||||
|
||||
SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
|
||||
SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
|
||||
|
||||
|
||||
|
@ -26,12 +26,7 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
int dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid);
|
||||
int dsp_close(struct snddev_info *d, int chan, int devtype);
|
||||
int dsp_read(struct snddev_info *d, int chan, struct uio *buf, int flag);
|
||||
int dsp_write(struct snddev_info *d, int chan, struct uio *buf, int flag);
|
||||
int dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg);
|
||||
int dsp_poll(struct snddev_info *d, int chan, int events, struct proc *p);
|
||||
int dsp_mmap(struct snddev_info *d, int chan, vm_offset_t offset, int nprot);
|
||||
int dsp_register(int unit, int channel);
|
||||
int dsp_unregister(int unit, int channel);
|
||||
|
||||
|
||||
|
@ -68,6 +68,39 @@ static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
|
||||
|
||||
static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
|
||||
|
||||
static d_open_t mixer_open;
|
||||
static d_close_t mixer_close;
|
||||
|
||||
static struct cdevsw mixer_cdevsw = {
|
||||
/* open */ mixer_open,
|
||||
/* close */ mixer_close,
|
||||
/* read */ noread,
|
||||
/* write */ nowrite,
|
||||
/* ioctl */ mixer_ioctl,
|
||||
/* poll */ nopoll,
|
||||
/* mmap */ nommap,
|
||||
/* strategy */ nostrategy,
|
||||
/* name */ "mixer",
|
||||
/* maj */ SND_CDEV_MAJOR,
|
||||
/* dump */ nodump,
|
||||
/* psize */ nopsize,
|
||||
/* flags */ 0,
|
||||
};
|
||||
|
||||
static eventhandler_tag mixer_ehtag;
|
||||
|
||||
static dev_t
|
||||
mixer_get_devt(device_t dev)
|
||||
{
|
||||
dev_t pdev;
|
||||
int unit;
|
||||
|
||||
unit = device_get_unit(dev);
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
mixer_lookup(char *devname)
|
||||
@ -156,32 +189,20 @@ mix_getdevinfo(struct snd_mixer *m)
|
||||
return m->devinfo;
|
||||
}
|
||||
|
||||
int
|
||||
mixer_busy(struct snd_mixer *m, int busy)
|
||||
{
|
||||
m->busy = busy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mixer_isbusy(struct snd_mixer *m)
|
||||
{
|
||||
return m->busy;
|
||||
}
|
||||
|
||||
int
|
||||
mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
u_int16_t v;
|
||||
int i;
|
||||
dev_t pdev;
|
||||
int i, unit;
|
||||
|
||||
m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
|
||||
snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
|
||||
m->lock = snd_mtxcreate(m->name);
|
||||
m->type = cls->name;
|
||||
m->devinfo = devinfo;
|
||||
m->busy = 0;
|
||||
|
||||
if (MIXER_INIT(m))
|
||||
goto bad;
|
||||
@ -193,8 +214,10 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
||||
|
||||
mixer_setrecsrc(m, SOUND_MASK_MIC);
|
||||
|
||||
d = device_get_softc(dev);
|
||||
d->mixer = m;
|
||||
unit = device_get_unit(dev);
|
||||
pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
|
||||
UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
|
||||
pdev->si_drv1 = m;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -209,13 +232,21 @@ int
|
||||
mixer_uninit(device_t dev)
|
||||
{
|
||||
int i;
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
dev_t pdev;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
m = d->mixer;
|
||||
pdev = mixer_get_devt(dev);
|
||||
m = pdev->si_drv1;
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
if (m->busy) {
|
||||
snd_mtxunlock(m->lock);
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
pdev->si_drv1 = NULL;
|
||||
destroy_dev(pdev);
|
||||
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
||||
mixer_set(m, i, 0);
|
||||
|
||||
@ -225,7 +256,6 @@ mixer_uninit(device_t dev)
|
||||
|
||||
snd_mtxfree(m->lock);
|
||||
kobj_delete((kobj_t)m, M_MIXER);
|
||||
d->mixer = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -233,12 +263,12 @@ mixer_uninit(device_t dev)
|
||||
int
|
||||
mixer_reinit(device_t dev)
|
||||
{
|
||||
int i;
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
dev_t pdev;
|
||||
int i;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
m = d->mixer;
|
||||
pdev = mixer_get_devt(dev);
|
||||
m = pdev->si_drv1;
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
i = MIXER_REINIT(m);
|
||||
@ -256,14 +286,146 @@ mixer_reinit(device_t dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
char devname[32];
|
||||
int error, dev;
|
||||
struct snd_mixer *m;
|
||||
|
||||
m = oidp->oid_arg1;
|
||||
snd_mtxlock(m->lock);
|
||||
strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
|
||||
error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
|
||||
if (error == 0 && req->newptr != NULL) {
|
||||
dev = mixer_lookup(devname);
|
||||
if (dev == -1) {
|
||||
snd_mtxunlock(m->lock);
|
||||
return EINVAL;
|
||||
}
|
||||
else if (dev != m->hwvol_mixer) {
|
||||
m->hwvol_mixer = dev;
|
||||
m->hwvol_muted = 0;
|
||||
}
|
||||
}
|
||||
snd_mtxunlock(m->lock);
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
mixer_ioctl(struct snddev_info *d, u_long cmd, caddr_t arg)
|
||||
mixer_hwvol_init(device_t dev)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
dev_t pdev;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
pdev = mixer_get_devt(dev);
|
||||
m = pdev->si_drv1;
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
m->hwvol_mixer = SOUND_MIXER_VOLUME;
|
||||
m->hwvol_step = 5;
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
|
||||
OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
|
||||
SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
|
||||
OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
|
||||
sysctl_hw_snd_hwvol_mixer, "A", "")
|
||||
#endif
|
||||
snd_mtxunlock(m->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
mixer_hwvol_mute(device_t dev)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
dev_t pdev;
|
||||
|
||||
pdev = mixer_get_devt(dev);
|
||||
m = pdev->si_drv1;
|
||||
snd_mtxlock(m->lock);
|
||||
if (m->hwvol_muted) {
|
||||
m->hwvol_muted = 0;
|
||||
mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
|
||||
} else {
|
||||
m->hwvol_muted++;
|
||||
m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
|
||||
mixer_set(m, m->hwvol_mixer, 0);
|
||||
}
|
||||
snd_mtxunlock(m->lock);
|
||||
}
|
||||
|
||||
void
|
||||
mixer_hwvol_step(device_t dev, int left_step, int right_step)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
int level, left, right;
|
||||
dev_t pdev;
|
||||
|
||||
pdev = mixer_get_devt(dev);
|
||||
m = pdev->si_drv1;
|
||||
snd_mtxlock(m->lock);
|
||||
if (m->hwvol_muted) {
|
||||
m->hwvol_muted = 0;
|
||||
level = m->hwvol_mute_level;
|
||||
} else
|
||||
level = mixer_get(m, m->hwvol_mixer);
|
||||
if (level != -1) {
|
||||
left = level & 0xff;
|
||||
right = level >> 8;
|
||||
left += left_step * m->hwvol_step;
|
||||
if (left < 0)
|
||||
left = 0;
|
||||
right += right_step * m->hwvol_step;
|
||||
if (right < 0)
|
||||
right = 0;
|
||||
mixer_set(m, m->hwvol_mixer, left | right << 8);
|
||||
}
|
||||
snd_mtxunlock(m->lock);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
mixer_open(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
|
||||
m = i_dev->si_drv1;
|
||||
if (m->busy)
|
||||
return EBUSY;
|
||||
m->busy = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_close(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
|
||||
m = i_dev->si_drv1;
|
||||
if (!m->busy)
|
||||
return EBADF;
|
||||
m->busy = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mixer_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
|
||||
{
|
||||
int ret, *arg_i = (int *)arg;
|
||||
int v = -1, j = cmd & 0xff;
|
||||
struct snd_mixer *m;
|
||||
|
||||
m = d->mixer;
|
||||
m = i_dev->si_drv1;
|
||||
if (!m->busy)
|
||||
return EBADF;
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
|
||||
@ -302,103 +464,34 @@ mixer_ioctl(struct snddev_info *d, u_long cmd, caddr_t arg)
|
||||
return ENXIO;
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
|
||||
static void
|
||||
mixer_clone(void *arg, char *name, int namelen, dev_t *dev)
|
||||
{
|
||||
char devname[32];
|
||||
int error, dev;
|
||||
struct snd_mixer *m;
|
||||
dev_t pdev;
|
||||
|
||||
m = oidp->oid_arg1;
|
||||
snd_mtxlock(m->lock);
|
||||
strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
|
||||
error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
|
||||
if (error == 0 && req->newptr != NULL) {
|
||||
dev = mixer_lookup(devname);
|
||||
if (dev == -1) {
|
||||
snd_mtxunlock(m->lock);
|
||||
return EINVAL;
|
||||
}
|
||||
else if (dev != m->hwvol_mixer) {
|
||||
m->hwvol_mixer = dev;
|
||||
m->hwvol_muted = 0;
|
||||
}
|
||||
if (*dev != NODEV)
|
||||
return;
|
||||
if (strcmp(name, "mixer") == 0) {
|
||||
pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(snd_unit, SND_DEV_CTL, 0));
|
||||
if (pdev->si_flags & SI_NAMED)
|
||||
*dev = pdev;
|
||||
}
|
||||
snd_mtxunlock(m->lock);
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
mixer_hwvol_init(device_t dev)
|
||||
static void
|
||||
mixer_sysinit(void *p)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
m = d->mixer;
|
||||
snd_mtxlock(m->lock);
|
||||
m->hwvol_mixer = SOUND_MIXER_VOLUME;
|
||||
m->hwvol_step = 5;
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
|
||||
OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
|
||||
SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
|
||||
OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
|
||||
sysctl_hw_snd_hwvol_mixer, "A", "")
|
||||
#endif
|
||||
snd_mtxunlock(m->lock);
|
||||
return 0;
|
||||
mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
|
||||
}
|
||||
|
||||
void
|
||||
mixer_hwvol_mute(device_t dev)
|
||||
static void
|
||||
mixer_sysuninit(void *p)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
m = d->mixer;
|
||||
snd_mtxlock(m->lock);
|
||||
if (m->hwvol_muted) {
|
||||
m->hwvol_muted = 0;
|
||||
mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
|
||||
} else {
|
||||
m->hwvol_muted++;
|
||||
m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
|
||||
mixer_set(m, m->hwvol_mixer, 0);
|
||||
}
|
||||
snd_mtxunlock(m->lock);
|
||||
if (mixer_ehtag != NULL)
|
||||
EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
|
||||
}
|
||||
|
||||
void
|
||||
mixer_hwvol_step(device_t dev, int left_step, int right_step)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
int level, left, right;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
m = d->mixer;
|
||||
snd_mtxlock(m->lock);
|
||||
if (m->hwvol_muted) {
|
||||
m->hwvol_muted = 0;
|
||||
level = m->hwvol_mute_level;
|
||||
} else
|
||||
level = mixer_get(m, m->hwvol_mixer);
|
||||
if (level != -1) {
|
||||
left = level & 0xff;
|
||||
right = level >> 8;
|
||||
left += left_step * m->hwvol_step;
|
||||
if (left < 0)
|
||||
left = 0;
|
||||
right += right_step * m->hwvol_step;
|
||||
if (right < 0)
|
||||
right = 0;
|
||||
mixer_set(m, m->hwvol_mixer, left | right << 8);
|
||||
}
|
||||
snd_mtxunlock(m->lock);
|
||||
}
|
||||
SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
|
||||
SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
|
||||
|
||||
|
||||
|
@ -29,9 +29,7 @@
|
||||
int mixer_init(device_t dev, kobj_class_t cls, void *devinfo);
|
||||
int mixer_uninit(device_t dev);
|
||||
int mixer_reinit(device_t dev);
|
||||
int mixer_ioctl(struct snddev_info *d, u_long cmd, caddr_t arg);
|
||||
int mixer_busy(struct snd_mixer *m, int busy);
|
||||
int mixer_isbusy(struct snd_mixer *m);
|
||||
int mixer_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p);
|
||||
|
||||
int mixer_hwvol_init(device_t dev);
|
||||
void mixer_hwvol_mute(device_t dev);
|
||||
|
240
sys/dev/sound/pcm/sndstat.c
Normal file
240
sys/dev/sound/pcm/sndstat.c
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk>
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <sys/sbuf.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
|
||||
static d_open_t sndstat_open;
|
||||
static d_close_t sndstat_close;
|
||||
static d_read_t sndstat_read;
|
||||
|
||||
static struct cdevsw sndstat_cdevsw = {
|
||||
/* open */ sndstat_open,
|
||||
/* close */ sndstat_close,
|
||||
/* read */ sndstat_read,
|
||||
/* write */ nowrite,
|
||||
/* ioctl */ noioctl,
|
||||
/* poll */ nopoll,
|
||||
/* mmap */ nommap,
|
||||
/* strategy */ nostrategy,
|
||||
/* name */ "sndstat",
|
||||
/* maj */ SND_CDEV_MAJOR,
|
||||
/* dump */ nodump,
|
||||
/* psize */ nopsize,
|
||||
/* flags */ 0,
|
||||
};
|
||||
|
||||
static struct sbuf sndstat_sbuf;
|
||||
static dev_t sndstat_dev = 0;
|
||||
static int sndstat_isopen = 0;
|
||||
static int sndstat_bufptr;
|
||||
|
||||
static int sndstat_verbose = 0;
|
||||
TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
|
||||
|
||||
static int sndstat_prepare(struct sbuf *s);
|
||||
|
||||
static int
|
||||
sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error, verbose;
|
||||
|
||||
verbose = sndstat_verbose;
|
||||
error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
|
||||
if (error == 0 && req->newptr != NULL) {
|
||||
if (verbose == 0 || verbose == 1)
|
||||
sndstat_verbose = verbose;
|
||||
else
|
||||
error = EINVAL;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
|
||||
0, sizeof(int), sysctl_hw_sndverbose, "I", "");
|
||||
|
||||
static int
|
||||
sndstat_open(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (sndstat_isopen)
|
||||
return EBUSY;
|
||||
if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL)
|
||||
return ENXIO;
|
||||
sndstat_bufptr = 0;
|
||||
err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM;
|
||||
if (!err)
|
||||
sndstat_isopen = 1;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
sndstat_close(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
if (!sndstat_isopen)
|
||||
return EBADF;
|
||||
sbuf_delete(&sndstat_sbuf);
|
||||
sndstat_isopen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sndstat_read(dev_t i_dev, struct uio *buf, int flag)
|
||||
{
|
||||
int l, err;
|
||||
|
||||
if (!sndstat_isopen)
|
||||
return EBADF;
|
||||
l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
|
||||
err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
|
||||
sndstat_bufptr += l;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
sndstat_prepare(struct sbuf *s)
|
||||
{
|
||||
int i, pc, rc, vc;
|
||||
device_t dev;
|
||||
struct snddev_info *d;
|
||||
struct snddev_channel *sce;
|
||||
struct pcm_channel *c;
|
||||
struct pcm_feeder *f;
|
||||
|
||||
sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\n", __DATE__, __TIME__);
|
||||
if (!pcm_devclass || devclass_get_maxunit(pcm_devclass) == 0) {
|
||||
sbuf_printf(s, "No devices installed.\n");
|
||||
sbuf_finish(s);
|
||||
return sbuf_len(s);
|
||||
} else
|
||||
sbuf_printf(s, "Installed devices:\n");
|
||||
|
||||
for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
|
||||
d = devclass_get_softc(pcm_devclass, i);
|
||||
if (!d)
|
||||
continue;
|
||||
snd_mtxlock(d->lock);
|
||||
dev = devclass_get_device(pcm_devclass, i);
|
||||
sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
|
||||
if (d->chancount > 0) {
|
||||
pc = rc = vc = 0;
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
if (c->direction == PCMDIR_PLAY) {
|
||||
if (c->flags & CHN_F_VIRTUAL)
|
||||
vc++;
|
||||
else
|
||||
pc++;
|
||||
} else
|
||||
rc++;
|
||||
}
|
||||
sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
|
||||
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
|
||||
#ifdef USING_DEVFS
|
||||
(i == snd_unit)? " default" : ""
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
if (!sndstat_verbose)
|
||||
goto skipverbose;
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
|
||||
c->parentchannel? c->parentchannel->name : "",
|
||||
c->name, c->speed, c->format, c->flags);
|
||||
if (c->pid != -1)
|
||||
sbuf_printf(s, ", pid %d", c->pid);
|
||||
sbuf_printf(s, "\n\t");
|
||||
f = c->feeder;
|
||||
while (f) {
|
||||
sbuf_printf(s, "%s", f->class->name);
|
||||
if (f->desc->type == FEEDER_FMT)
|
||||
sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
|
||||
if (f->desc->type == FEEDER_RATE)
|
||||
sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
|
||||
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
|
||||
sbuf_printf(s, "(%08x)", f->desc->out);
|
||||
if (f->source)
|
||||
sbuf_printf(s, " <- ");
|
||||
f = f->source;
|
||||
}
|
||||
sbuf_printf(s, "\n");
|
||||
}
|
||||
skipverbose:
|
||||
} else
|
||||
sbuf_printf(s, " (mixer only)\n");
|
||||
snd_mtxunlock(d->lock);
|
||||
}
|
||||
sbuf_finish(s);
|
||||
return sbuf_len(s);
|
||||
}
|
||||
|
||||
static int
|
||||
sndstat_init(void)
|
||||
{
|
||||
sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat");
|
||||
|
||||
return (sndstat_dev != 0)? 0 : ENXIO;
|
||||
}
|
||||
|
||||
static int
|
||||
sndstat_uninit(void)
|
||||
{
|
||||
if (sndstat_isopen)
|
||||
return EBUSY;
|
||||
|
||||
if (sndstat_dev)
|
||||
destroy_dev(sndstat_dev);
|
||||
sndstat_dev = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sndstat_sysinit(void *p)
|
||||
{
|
||||
sndstat_init();
|
||||
}
|
||||
|
||||
static void
|
||||
sndstat_sysuninit(void *p)
|
||||
{
|
||||
sndstat_uninit();
|
||||
}
|
||||
|
||||
SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysinit, NULL);
|
||||
SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysuninit, NULL);
|
||||
|
||||
|
@ -30,70 +30,11 @@
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/sbuf.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
|
||||
#undef SNDSTAT_VERBOSE
|
||||
#define PCM_MAXCHANS 256
|
||||
|
||||
static dev_t status_dev = 0;
|
||||
static int do_status(int action, struct uio *buf);
|
||||
|
||||
static d_open_t sndopen;
|
||||
static d_close_t sndclose;
|
||||
static d_ioctl_t sndioctl;
|
||||
static d_read_t sndread;
|
||||
static d_write_t sndwrite;
|
||||
static d_mmap_t sndmmap;
|
||||
static d_poll_t sndpoll;
|
||||
|
||||
#define CDEV_MAJOR 30
|
||||
static struct cdevsw snd_cdevsw = {
|
||||
/* open */ sndopen,
|
||||
/* close */ sndclose,
|
||||
/* read */ sndread,
|
||||
/* write */ sndwrite,
|
||||
/* ioctl */ sndioctl,
|
||||
/* poll */ sndpoll,
|
||||
/* mmap */ sndmmap,
|
||||
/* strategy */ nostrategy,
|
||||
/* name */ "snd",
|
||||
/* maj */ CDEV_MAJOR,
|
||||
/* dump */ nodump,
|
||||
/* psize */ nopsize,
|
||||
/* flags */ D_TRACKCLOSE,
|
||||
};
|
||||
|
||||
/*
|
||||
PROPOSAL:
|
||||
each unit needs:
|
||||
status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
|
||||
dspW and audio are deprecated.
|
||||
dsp needs min 64 channels, will give it 256
|
||||
|
||||
minor = (unit << 20) + (dev << 16) + channel
|
||||
currently minor = (channel << 16) + (unit << 4) + dev
|
||||
|
||||
nomenclature:
|
||||
/dev/pcmX/dsp.(0..255)
|
||||
/dev/pcmX/dspW
|
||||
/dev/pcmX/audio
|
||||
/dev/pcmX/status
|
||||
/dev/pcmX/mixer
|
||||
[etc.]
|
||||
*/
|
||||
|
||||
#define PCMMINOR(x) (minor(x))
|
||||
#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
|
||||
#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
|
||||
#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
|
||||
#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
|
||||
|
||||
static devclass_t pcm_devclass;
|
||||
devclass_t pcm_devclass;
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
static int snd_unit = 0;
|
||||
int snd_unit = 0;
|
||||
TUNABLE_INT("hw.snd.unit", &snd_unit);
|
||||
#endif
|
||||
|
||||
@ -212,79 +153,6 @@ pcm_chnref(struct pcm_channel *c, int ref)
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
static void
|
||||
snd_setdefaultunit(struct snddev_info *d)
|
||||
{
|
||||
static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
|
||||
|
||||
if (dsp) {
|
||||
destroy_dev(dsp);
|
||||
dsp = 0;
|
||||
}
|
||||
if (dspW) {
|
||||
destroy_dev(dspW);
|
||||
dspW = 0;
|
||||
}
|
||||
if (audio) {
|
||||
destroy_dev(audio);
|
||||
audio = 0;
|
||||
}
|
||||
if (mixer) {
|
||||
destroy_dev(mixer);
|
||||
mixer = 0;
|
||||
}
|
||||
|
||||
if (d == NULL)
|
||||
return;
|
||||
|
||||
if (d->dspdev)
|
||||
dsp = make_dev_alias(d->dspdev, "dsp");
|
||||
if (d->dspWdev)
|
||||
dspW = make_dev_alias(d->dspWdev, "dspW");
|
||||
if (d->audiodev)
|
||||
audio = make_dev_alias(d->audiodev, "audio");
|
||||
if (d->mixerdev)
|
||||
mixer = make_dev_alias(d->mixerdev, "mixer");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
pcm_relinkdspunit(struct snddev_info *d)
|
||||
{
|
||||
#ifdef USING_DEVFS
|
||||
int unit = device_get_unit(d->dev);
|
||||
dev_t pdev;
|
||||
|
||||
if (d->dspdev) {
|
||||
destroy_dev(d->dspdev);
|
||||
d->dspdev = 0;
|
||||
}
|
||||
if (d->dspWdev) {
|
||||
destroy_dev(d->dspWdev);
|
||||
d->dspWdev = 0;
|
||||
}
|
||||
if (d->audiodev) {
|
||||
destroy_dev(d->audiodev);
|
||||
d->audiodev = 0;
|
||||
}
|
||||
|
||||
if (d->defaultchan < d->chancount) {
|
||||
pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->defaultchan));
|
||||
d->dspdev = make_dev_alias(pdev, "dsp%d", unit);
|
||||
|
||||
pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->defaultchan));
|
||||
d->dspWdev = make_dev_alias(pdev, "dspW%d", unit);
|
||||
|
||||
pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->defaultchan));
|
||||
d->audiodev = make_dev_alias(pdev, "audio%d", unit);
|
||||
}
|
||||
|
||||
if (unit == snd_unit)
|
||||
snd_setdefaultunit(d);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
static int
|
||||
sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
|
||||
@ -301,7 +169,6 @@ sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
|
||||
if (d == NULL || d->chancount == 0)
|
||||
return EINVAL;
|
||||
snd_unit = unit;
|
||||
snd_setdefaultunit(d);
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
@ -378,36 +245,9 @@ int
|
||||
pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
|
||||
{
|
||||
struct snddev_channel *sce;
|
||||
struct pcm_channel **aplay, **arec;
|
||||
int unit = device_get_unit(d->dev);
|
||||
int cc, sz;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
if (d->chancount == d->maxchans) {
|
||||
cc = d->maxchans? d->maxchans * 2 : 2;
|
||||
sz = cc * sizeof(struct pcm_channel *);
|
||||
aplay = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
arec = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (aplay == NULL || arec == NULL) {
|
||||
if (aplay)
|
||||
free(aplay, M_DEVBUF);
|
||||
if (arec)
|
||||
free(arec, M_DEVBUF);
|
||||
snd_mtxunlock(d->lock);
|
||||
return EINVAL;
|
||||
}
|
||||
if (d->aplay) {
|
||||
bcopy(d->aplay, aplay, d->maxchans * sizeof(struct pcm_channel *));
|
||||
free(d->aplay, M_DEVBUF);
|
||||
}
|
||||
d->aplay = aplay;
|
||||
if (d->arec) {
|
||||
bcopy(d->arec, arec, d->maxchans * sizeof(struct pcm_channel *));
|
||||
free(d->arec, M_DEVBUF);
|
||||
}
|
||||
d->arec = arec;
|
||||
d->maxchans = cc;
|
||||
}
|
||||
|
||||
sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (!sce) {
|
||||
@ -418,16 +258,8 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
|
||||
sce->channel = ch;
|
||||
SLIST_INSERT_HEAD(&d->channels, sce, link);
|
||||
|
||||
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
|
||||
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
|
||||
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
|
||||
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
|
||||
/* XXX SND_DEV_NORESET? */
|
||||
|
||||
if (d->chancount++ == 0)
|
||||
pcm_relinkdspunit(d);
|
||||
dsp_register(unit, d->chancount);
|
||||
d->chancount++;
|
||||
|
||||
snd_mtxunlock(d->lock);
|
||||
|
||||
@ -439,7 +271,6 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
|
||||
{
|
||||
struct snddev_channel *sce;
|
||||
int unit = device_get_unit(d->dev);
|
||||
dev_t pdev;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
@ -453,14 +284,7 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
|
||||
SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
|
||||
free(sce, M_DEVBUF);
|
||||
|
||||
if (d->chancount == 0)
|
||||
pcm_relinkdspunit(d);
|
||||
pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
|
||||
destroy_dev(pdev);
|
||||
pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
|
||||
destroy_dev(pdev);
|
||||
pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
|
||||
destroy_dev(pdev);
|
||||
dsp_unregister(unit, d->chancount);
|
||||
snd_mtxunlock(d->lock);
|
||||
|
||||
return 0;
|
||||
@ -523,13 +347,7 @@ void
|
||||
pcm_setflags(device_t dev, u_int32_t val)
|
||||
{
|
||||
struct snddev_info *d = device_get_softc(dev);
|
||||
/*
|
||||
if ((val & SD_F_SIMPLEX) && (d->fakechan == NULL)) {
|
||||
device_printf(dev, "set simplex mode\n");
|
||||
d->fakechan = fkchan_setup(dev);
|
||||
chn_init(d->fakechan, NULL, 0);
|
||||
}
|
||||
*/
|
||||
|
||||
d->flags = val;
|
||||
}
|
||||
|
||||
@ -545,42 +363,20 @@ pcm_getdevinfo(device_t dev)
|
||||
int
|
||||
pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
{
|
||||
int unit = device_get_unit(dev);
|
||||
struct snddev_info *d = device_get_softc(dev);
|
||||
|
||||
d->lock = snd_mtxcreate(device_get_nameunit(dev));
|
||||
snd_mtxlock(d->lock);
|
||||
if (!pcm_devclass) {
|
||||
pcm_devclass = device_get_devclass(dev);
|
||||
status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
|
||||
UID_ROOT, GID_WHEEL, 0444, "sndstat");
|
||||
}
|
||||
|
||||
d->mixerdev = make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
|
||||
UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
|
||||
|
||||
d->dspdev = 0;
|
||||
d->dspWdev = 0;
|
||||
d->audiodev = 0;
|
||||
d->dev = dev;
|
||||
d->devinfo = devinfo;
|
||||
d->chancount = 0;
|
||||
d->defaultchan = 0;
|
||||
d->maxchans = 0;
|
||||
d->aplay = NULL;
|
||||
d->arec = NULL;
|
||||
/*
|
||||
sz = d->maxchans * sizeof(struct pcm_channel *);
|
||||
d->inprog = 0;
|
||||
|
||||
if (sz > 0) {
|
||||
d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (!d->arec || !d->aplay) goto no;
|
||||
}
|
||||
*/
|
||||
if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) {
|
||||
if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
|
||||
d->flags |= SD_F_SIMPLEX;
|
||||
}
|
||||
|
||||
d->fakechan = fkchan_setup(dev);
|
||||
chn_init(d->fakechan, NULL, 0);
|
||||
|
||||
@ -594,19 +390,11 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
goto no;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
if (numplay > 0)
|
||||
vchan_initsys(d);
|
||||
#endif
|
||||
snd_setdefaultunit(d);
|
||||
snd_mtxunlock(d->lock);
|
||||
return 0;
|
||||
no:
|
||||
/*
|
||||
if (d->aplay) free(d->aplay, M_DEVBUF);
|
||||
if (d->arec) free(d->arec, M_DEVBUF);
|
||||
*/
|
||||
/* snd_mtxunlock(d->lock); */
|
||||
snd_mtxfree(d->lock);
|
||||
return ENXIO;
|
||||
}
|
||||
@ -614,11 +402,15 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
int
|
||||
pcm_unregister(device_t dev)
|
||||
{
|
||||
int unit = device_get_unit(dev);
|
||||
struct snddev_info *d = device_get_softc(dev);
|
||||
struct snddev_channel *sce;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
if (d->inprog) {
|
||||
device_printf(dev, "unregister: operation in progress");
|
||||
snd_mtxunlock(d->lock);
|
||||
return EBUSY;
|
||||
}
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
if (sce->channel->refcount > 0) {
|
||||
device_printf(dev, "unregister: channel busy");
|
||||
@ -626,7 +418,7 @@ pcm_unregister(device_t dev)
|
||||
return EBUSY;
|
||||
}
|
||||
}
|
||||
if (mixer_isbusy(d->mixer)) {
|
||||
if (mixer_uninit(dev)) {
|
||||
device_printf(dev, "unregister: mixer busy");
|
||||
snd_mtxunlock(d->lock);
|
||||
return EBUSY;
|
||||
@ -636,381 +428,19 @@ pcm_unregister(device_t dev)
|
||||
d->sysctl_tree_top = NULL;
|
||||
sysctl_ctx_free(&d->sysctl_tree);
|
||||
#endif
|
||||
if (unit == snd_unit)
|
||||
snd_setdefaultunit(NULL);
|
||||
destroy_dev(d->mixerdev);
|
||||
mixer_uninit(dev);
|
||||
|
||||
while (d->chancount > 0)
|
||||
pcm_killchan(dev);
|
||||
|
||||
if (d->aplay) free(d->aplay, M_DEVBUF);
|
||||
if (d->arec) free(d->arec, M_DEVBUF);
|
||||
|
||||
chn_kill(d->fakechan);
|
||||
fkchan_kill(d->fakechan);
|
||||
|
||||
/* snd_mtxunlock(d->lock); */
|
||||
snd_mtxfree(d->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* a small utility function which, given a device number, returns
|
||||
* a pointer to the associated struct snddev_info struct, and sets the unit
|
||||
* number.
|
||||
*/
|
||||
static struct snddev_info *
|
||||
get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
|
||||
{
|
||||
struct snddev_info *sc;
|
||||
int u, d, c;
|
||||
|
||||
u = PCMUNIT(i_dev);
|
||||
d = PCMDEV(i_dev);
|
||||
c = PCMCHAN(i_dev);
|
||||
if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
|
||||
if (unit) *unit = u;
|
||||
if (dev) *dev = d;
|
||||
if (chan) *chan = c;
|
||||
if (u < 0) return NULL;
|
||||
|
||||
sc = devclass_get_softc(pcm_devclass, u);
|
||||
if (sc == NULL) return NULL;
|
||||
|
||||
switch(d) {
|
||||
case SND_DEV_CTL: /* /dev/mixer handled by pcm */
|
||||
case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
case SND_DEV_AUDIO:
|
||||
return sc;
|
||||
|
||||
case SND_DEV_SEQ: /* XXX when enabled... */
|
||||
case SND_DEV_SEQ2:
|
||||
case SND_DEV_MIDIN:
|
||||
case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */
|
||||
default:
|
||||
printf("unsupported subdevice %d\n", d);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
int dev, unit, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
|
||||
|
||||
DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
|
||||
unit, dev, flags, mode));
|
||||
|
||||
switch(dev) {
|
||||
case SND_DEV_STATUS:
|
||||
return do_status(0, NULL);
|
||||
|
||||
case SND_DEV_CTL:
|
||||
return d? mixer_busy(d->mixer, 1) : ENXIO;
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
case SND_DEV_NORESET:
|
||||
return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO;
|
||||
|
||||
default:
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
|
||||
{
|
||||
int dev, unit, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
|
||||
|
||||
DEB(printf("close snd%d subdev %d\n", unit, dev));
|
||||
|
||||
switch(dev) { /* only those for which close makes sense */
|
||||
case SND_DEV_STATUS:
|
||||
return do_status(1, NULL);
|
||||
|
||||
case SND_DEV_CTL:
|
||||
return d? mixer_busy(d->mixer, 0) : ENXIO;
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
return d? dsp_close(d, chan, dev) : ENXIO;
|
||||
|
||||
default:
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sndread(dev_t i_dev, struct uio *buf, int flag)
|
||||
{
|
||||
int dev, unit, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
|
||||
DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
|
||||
|
||||
switch(dev) {
|
||||
case SND_DEV_STATUS:
|
||||
return do_status(2, buf);
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
return d? dsp_read(d, chan, buf, flag) : EBADF;
|
||||
|
||||
default:
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sndwrite(dev_t i_dev, struct uio *buf, int flag)
|
||||
{
|
||||
int dev, unit, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
|
||||
|
||||
DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
|
||||
|
||||
switch(dev) { /* only writeable devices */
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
case SND_DEV_AUDIO:
|
||||
return d? dsp_write(d, chan, buf, flag) : EBADF;
|
||||
|
||||
default:
|
||||
return EPERM; /* for non-writeable devices ; */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
|
||||
{
|
||||
int dev, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
|
||||
|
||||
if (d == NULL) return ENXIO;
|
||||
|
||||
switch(dev) {
|
||||
case SND_DEV_CTL:
|
||||
return mixer_ioctl(d, cmd, arg);
|
||||
|
||||
case SND_DEV_AUDIO:
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
if (IOCGROUP(cmd) == 'M')
|
||||
return mixer_ioctl(d, cmd, arg);
|
||||
else
|
||||
return dsp_ioctl(d, chan, cmd, arg);
|
||||
|
||||
default:
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sndpoll(dev_t i_dev, int events, struct proc *p)
|
||||
{
|
||||
int dev, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
|
||||
|
||||
DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
|
||||
|
||||
if (d == NULL) return ENXIO;
|
||||
|
||||
switch(dev) {
|
||||
case SND_DEV_AUDIO:
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
return dsp_poll(d, chan, events, p);
|
||||
|
||||
default:
|
||||
return (events &
|
||||
(POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The mmap interface allows access to the play and read buffer,
|
||||
* plus the device descriptor.
|
||||
* The various blocks are accessible at the following offsets:
|
||||
*
|
||||
* 0x00000000 ( 0 ) : write buffer ;
|
||||
* 0x01000000 (16 MB) : read buffer ;
|
||||
* 0x02000000 (32 MB) : device descriptor (dangerous!)
|
||||
*
|
||||
* WARNING: the mmap routines assume memory areas are aligned. This
|
||||
* is true (probably) for the dma buffers, but likely false for the
|
||||
* device descriptor. As a consequence, we do not know where it is
|
||||
* located in the requested area.
|
||||
*/
|
||||
static int
|
||||
sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
|
||||
{
|
||||
int unit, dev, chan;
|
||||
struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
|
||||
|
||||
DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
|
||||
d, dev, offset, nprot));
|
||||
|
||||
if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */
|
||||
|
||||
switch(dev) {
|
||||
case SND_DEV_AUDIO:
|
||||
case SND_DEV_DSP:
|
||||
case SND_DEV_DSP16:
|
||||
return dsp_mmap(d, chan, offset, nprot);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
status_init(struct sbuf *s)
|
||||
{
|
||||
int i, pc, rc, vc;
|
||||
device_t dev;
|
||||
struct snddev_info *d;
|
||||
struct snddev_channel *sce;
|
||||
struct pcm_channel *c;
|
||||
#ifdef SNDSTAT_VERBOSE
|
||||
struct pcm_feeder *f;
|
||||
#endif
|
||||
|
||||
sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n",
|
||||
__DATE__, __TIME__);
|
||||
|
||||
for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
|
||||
d = devclass_get_softc(pcm_devclass, i);
|
||||
if (!d)
|
||||
continue;
|
||||
snd_mtxlock(d->lock);
|
||||
dev = devclass_get_device(pcm_devclass, i);
|
||||
sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
|
||||
if (d->chancount > 0) {
|
||||
pc = rc = vc = 0;
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
if (c->direction == PCMDIR_PLAY) {
|
||||
if (c->flags & CHN_F_VIRTUAL)
|
||||
vc++;
|
||||
else
|
||||
pc++;
|
||||
} else
|
||||
rc++;
|
||||
}
|
||||
sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
|
||||
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
|
||||
#ifdef USING_DEVFS
|
||||
(i == snd_unit)? " default" : ""
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
#ifdef SNDSTAT_VERBOSE
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
|
||||
c->parentchannel? c->parentchannel->name : "",
|
||||
c->name, c->speed, c->format, c->flags);
|
||||
if (c->pid != -1)
|
||||
sbuf_printf(s, ", pid %d", c->pid);
|
||||
sbuf_printf(s, "\n\t");
|
||||
f = c->feeder;
|
||||
while (f) {
|
||||
sbuf_printf(s, "%s", f->class->name);
|
||||
if (f->desc->type == FEEDER_FMT)
|
||||
sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
|
||||
if (f->desc->type == FEEDER_RATE)
|
||||
sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
|
||||
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
|
||||
sbuf_printf(s, "(%08x)", f->desc->out);
|
||||
if (f->source)
|
||||
sbuf_printf(s, " <- ");
|
||||
f = f->source;
|
||||
}
|
||||
sbuf_printf(s, "\n");
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
sbuf_printf(s, " (mixer only)\n");
|
||||
snd_mtxunlock(d->lock);
|
||||
}
|
||||
sbuf_finish(s);
|
||||
return sbuf_len(s);
|
||||
}
|
||||
|
||||
static int
|
||||
do_status(int action, struct uio *buf)
|
||||
{
|
||||
static struct sbuf s;
|
||||
static int bufptr = 0;
|
||||
static int status_open = 0;
|
||||
int l, err;
|
||||
|
||||
switch(action) {
|
||||
case 0: /* open */
|
||||
if (status_open)
|
||||
return EBUSY;
|
||||
if (sbuf_new(&s, NULL, 4096, 0) == NULL)
|
||||
return ENXIO;
|
||||
bufptr = 0;
|
||||
err = (status_init(&s) > 0)? 0 : ENOMEM;
|
||||
if (!err)
|
||||
status_open = 1;
|
||||
return err;
|
||||
|
||||
case 1: /* close */
|
||||
if (!status_open)
|
||||
return EBADF;
|
||||
sbuf_delete(&s);
|
||||
status_open = 0;
|
||||
return 0;
|
||||
|
||||
case 2:
|
||||
if (!status_open)
|
||||
return EBADF;
|
||||
l = min(buf->uio_resid, sbuf_len(&s) - bufptr);
|
||||
err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0;
|
||||
bufptr += l;
|
||||
return err;
|
||||
|
||||
case 3:
|
||||
return status_open;
|
||||
}
|
||||
|
||||
return EBADF;
|
||||
}
|
||||
|
||||
static int
|
||||
sndpcm_modevent(module_t mod, int type, void *data)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
break;
|
||||
case MOD_UNLOAD:
|
||||
if (do_status(3, NULL))
|
||||
return EBUSY;
|
||||
if (status_dev)
|
||||
destroy_dev(status_dev);
|
||||
status_dev = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static moduledata_t sndpcm_mod = {
|
||||
"snd_pcm",
|
||||
sndpcm_modevent,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
|
||||
|
@ -107,16 +107,16 @@ struct snddev_channel {
|
||||
/* descriptor of audio device */
|
||||
struct snddev_info {
|
||||
SLIST_HEAD(, snddev_channel) channels;
|
||||
struct pcm_channel **aplay, **arec, *fakechan;
|
||||
unsigned chancount, maxchans, defaultchan;
|
||||
struct snd_mixer *mixer;
|
||||
struct pcm_channel *fakechan;
|
||||
unsigned chancount, defaultchan;
|
||||
/* struct snd_mixer *mixer; */
|
||||
unsigned flags;
|
||||
int inprog;
|
||||
void *devinfo;
|
||||
device_t dev;
|
||||
char status[SND_STATUSLEN];
|
||||
struct sysctl_ctx_list sysctl_tree;
|
||||
struct sysctl_oid *sysctl_tree_top;
|
||||
dev_t dspdev, dspWdev, audiodev, mixerdev;
|
||||
void *lock;
|
||||
};
|
||||
|
||||
@ -132,7 +132,30 @@ struct snddev_info {
|
||||
#define PCM_PREFVER PCM_MODVER
|
||||
#define PCM_MAXVER 1
|
||||
|
||||
#define MAGIC(unit) (0xa4d10de0 + unit)
|
||||
/*
|
||||
PROPOSAL:
|
||||
each unit needs:
|
||||
status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
|
||||
dspW and audio are deprecated.
|
||||
dsp needs min 64 channels, will give it 256
|
||||
|
||||
minor = (unit << 20) + (dev << 16) + channel
|
||||
currently minor = (channel << 16) + (unit << 4) + dev
|
||||
|
||||
nomenclature:
|
||||
/dev/pcmX/dsp.(0..255)
|
||||
/dev/pcmX/dspW
|
||||
/dev/pcmX/audio
|
||||
/dev/pcmX/status
|
||||
/dev/pcmX/mixer
|
||||
[etc.]
|
||||
*/
|
||||
|
||||
#define PCMMINOR(x) (minor(x))
|
||||
#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
|
||||
#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
|
||||
#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
|
||||
#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
|
||||
|
||||
#define SD_F_SIMPLEX 0x00000001
|
||||
#define SD_F_PRIO_RD 0x10000000
|
||||
@ -156,6 +179,11 @@ struct snddev_info {
|
||||
struct pcm_channel *fkchan_setup(device_t dev);
|
||||
int fkchan_kill(struct pcm_channel *c);
|
||||
|
||||
/*
|
||||
* Major nuber for the sound driver.
|
||||
*/
|
||||
#define SND_CDEV_MAJOR 30
|
||||
|
||||
/*
|
||||
* Minor numbers for the sound driver.
|
||||
*
|
||||
@ -185,6 +213,9 @@ int fkchan_kill(struct pcm_channel *c);
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
extern int snd_unit;
|
||||
extern devclass_t pcm_devclass;
|
||||
|
||||
/*
|
||||
* some macros for debugging purposes
|
||||
* DDB/DEB to enable/disable debugging stuff
|
||||
@ -224,7 +255,6 @@ void snd_mtxfree(void *m);
|
||||
void snd_mtxassert(void *m);
|
||||
void snd_mtxlock(void *m);
|
||||
void snd_mtxunlock(void *m);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
/* usage of flags in device config entry (config file) */
|
||||
|
Loading…
Reference in New Issue
Block a user