Index: src/sys/miscfs/specfs/spec_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/specfs/spec_vnops.c,v
retrieving revision 1.162
diff -u -r1.162 spec_vnops.c
--- src/sys/miscfs/specfs/spec_vnops.c 4 Apr 2016 08:03:53 -0000 1.162
+++ src/sys/miscfs/specfs/spec_vnops.c 15 Apr 2016 13:01:13 -0000
@@ -1228,7 +1228,7 @@
sd->sd_bdevvp = NULL;
mutex_exit(&device_lock);
- if (count != 0)
+ if (count != 0 && (vp->v_type != VCHR || cdev_type(dev) != D_MCLOSE))
return 0;
/*
Index: src/sys/sys/conf.h
===================================================================
RCS file: /cvsroot/src/sys/sys/conf.h,v
retrieving revision 1.146
diff -u -r1.146 conf.h
--- src/sys/sys/conf.h 17 Jan 2016 23:16:46 -0000 1.146
+++ src/sys/sys/conf.h 15 Apr 2016 13:01:45 -0000
@@ -60,6 +60,7 @@
#define D_TAPE 0x0001
#define D_DISK 0x0002
#define D_TTY 0x0003
+#define D_MCLOSE 0x0004
#define D_TYPEMASK 0x00ff
#define D_MPSAFE 0x0100
#define D_NEGOFFSAFE 0x0200
Index: src/sys/dev/audiobell.c
===================================================================
RCS file: /cvsroot/src/sys/dev/audiobell.c,v
retrieving revision 1.8
diff -u -r1.8 audiobell.c
--- src/sys/dev/audiobell.c 12 May 2009 10:22:31 -0000 1.8
+++ src/sys/dev/audiobell.c 25 May 2016 12:14:24 -0000
@@ -1,5 +1,6 @@
/* $NetBSD: audiobell.c,v 1.8 2009/05/12 10:22:31 cegger Exp $ */
+
/*
* Copyright (c) 1999 Richard Earnshaw
* Copyright (c) 2004 Ben Harris
@@ -37,6 +38,7 @@
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/fcntl.h>
+#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/null.h>
#include <sys/systm.h>
@@ -46,6 +48,7 @@
#include <dev/audiobellvar.h>
extern dev_type_open(audioopen);
+extern dev_type_ioctl(audioioctl);
extern dev_type_write(audiowrite);
extern dev_type_close(audioclose);
@@ -138,8 +141,10 @@
{
device_t audio = arg;
uint8_t *buf;
+ struct audio_info ai;
struct uio auio;
struct iovec aiov;
+ int size, len, offset;
/* The audio system isn't built for polling. */
if (poll) return;
@@ -148,21 +153,32 @@
if (audioopen(AUDIO_DEVICE | device_unit(audio), FWRITE, 0, NULL) != 0)
return;
- buf = malloc(period * 8, M_TEMP, M_WAITOK);
- if (buf == NULL) goto out;
- if (audiobell_synthesize(buf, pitch, period, volume) != 0) goto out;
-
- aiov.iov_base = (void *)buf;
- aiov.iov_len = period * 8;
- auio.uio_iov = &aiov;
- auio.uio_iovcnt = 1;
- auio.uio_offset = 0;
- auio.uio_resid = period * 8;
- auio.uio_rw = UIO_WRITE;
- UIO_SETUP_SYSSPACE(&auio);
+ if (audioioctl((dev_t)(AUDIO_DEVICE | device_unit(audio)),
+ AUDIO_GETINFO, &ai, 0, NULL) != 0)
+ return;
- audiowrite(AUDIO_DEVICE | device_unit(audio), &auio, 0);
+ buf = malloc(ai.blocksize, M_TEMP, M_WAITOK);
+ if (buf == NULL) goto out;
+ len = period * 8;
+ offset = 0;
+ while (len > 0) {
+ size = min(len, ai.blocksize);
+ if (audiobell_synthesize(buf, pitch, size / 8, volume) != 0)
+ goto out;
+ aiov.iov_base = (void *)buf;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = 0;
+ auio.uio_resid = size;
+ auio.uio_rw = UIO_WRITE;
+ UIO_SETUP_SYSSPACE(&auio);
+
+ audiowrite(AUDIO_DEVICE | device_unit(audio), &auio, 0);
+ len -= size;
+ offset += size;
+ }
out:
if (buf != NULL) free(buf, M_TEMP);
audioclose(AUDIO_DEVICE | device_unit(audio), FWRITE, 0, NULL);
Index: src/sys/dev/files.audio
===================================================================
RCS file: /cvsroot/src/sys/dev/files.audio,v
retrieving revision 1.3
diff -u -r1.3 files.audio
--- src/sys/dev/files.audio 18 Nov 2014 01:53:17 -0000 1.3
+++ src/sys/dev/files.audio 25 May 2016 12:14:53 -0000
@@ -13,10 +13,12 @@
# audio and midi devices, attaches to audio hardware driver
#
-device audio: audiodev
+device audio {}: audiodev
attach audio at audiobus
device midi: audio
attach midi at midibus
+device spkr: audiobell
+attach spkr at audio
# console bell via audio device
#
@@ -31,3 +33,4 @@
file dev/midictl.c midisyn
file dev/midisyn.c midisyn
file dev/mulaw.c mulaw needs-flag
+file dev/isa/spkr.c spkr
Index: src/sys/conf/majors
===================================================================
RCS file: /cvsroot/src/sys/conf/majors,v
retrieving revision 1.73
diff -u -r1.73 majors
--- src/sys/conf/majors 13 May 2016 07:41:47 -0000 1.73
+++ src/sys/conf/majors 25 May 2016 12:15:21 -0000
@@ -9,6 +9,7 @@
#
# Majors 160-511 are used for the MI drivers.
+device-major spkr char 156 spkr
device-major crypto char 160 crypto single
device-major pf char 161 pf single
#obsolete vinum char 162 vinum
Index: src/sys/arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.433
diff -u -r1.433 GENERIC
--- src/sys/arch/amd64/conf/GENERIC 14 May 2016 17:11:30 -0000 1.433
+++ src/sys/arch/amd64/conf/GENERIC 25 May 2016 12:16:40 -0000
@@ -1099,6 +1103,12 @@
# Audio support
audio* at audiobus?
+options VAUDIO
+#vaudio* at audio?
+
+# The spkr driver provides a simple tone interface to the built in speaker.
+options VAUDIOSPEAKER
+spkr0 at audio0 # PC speaker
# MPU 401 UARTs
#mpu* at isa? port 0x330 irq 9 # MPU401 or compatible card
@@ -1110,10 +1120,6 @@
midi* at midibus?
midi* at pcppi? # MIDI interface to the PC speaker
-# The spkr driver provides a simple tone interface to the built in speaker.
-#spkr0 at pcppi? # PC speaker
-
-
# FM-Radio devices
# PCI radio devices
#gtp* at pci? dev ? function ? # Guillemot Maxi Radio FM 2000 Radio Card
Index: src/sys/dev/isa/spkr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/isa/spkr.c,v
retrieving revision 1.36
diff -u -r1.36 spkr.c
--- src/sys/dev/isa/spkr.c 17 May 2015 05:20:37 -0000 1.36
+++ src/sys/dev/isa/spkr.c 21 Apr 2016 12:20:39 -0000
@@ -56,34 +56,51 @@
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/kthread.h>
+
+struct vbell_args {
+ device_t *cookie;
+ u_int pitch;
+ u_int period;
+ u_int volume;
+ bool dying;
+};
-#include <sys/bus.h>
+void bell_thread(void *);
-#include <dev/isa/pcppivar.h>
+#include <dev/audiobellvar.h>
+#include <dev/spkrvar.h>
#include <dev/isa/spkrio.h>
int spkrprobe(device_t, cfdata_t, void *);
void spkrattach(device_t, device_t, void *);
int spkrdetach(device_t, int);
+device_t speakerattach_mi(device_t);
#include "ioconf.h"
-MODULE(MODULE_CLASS_DRIVER, spkr, NULL /* "pcppi" */);
+MODULE(MODULE_CLASS_DRIVER, spkr, NULL /* "audio" */);
#ifdef _MODULE
#include "ioconf.c"
#endif
-CFATTACH_DECL_NEW(spkr, 0,
- spkrprobe, spkrattach, spkrdetach, NULL);
+CFATTACH_DECL3_NEW(spkr, 0,
+ spkrprobe, spkrattach, spkrdetach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
dev_type_open(spkropen);
dev_type_close(spkrclose);
dev_type_write(spkrwrite);
dev_type_ioctl(spkrioctl);
+struct spkr_attach_args {
+ device_t dev;
+};
+
const struct cdevsw spkr_cdevsw = {
.d_open = spkropen,
.d_close = spkrclose,
@@ -99,7 +116,12 @@
.d_flag = D_OTHER
};
-static pcppi_tag_t ppicookie;
+device_t vacookie;
+device_t *vaudiocookie;
+struct vbell_args sc_bell_args;
+lwp_t *sc_bellthread;
+kmutex_t sc_bellock;
+kcondvar_t sc_bellcv;
#define SPKRPRI (PZERO - 1)
@@ -113,7 +135,7 @@
tone(u_int xhz, u_int ticks)
/* emit tone of frequency hz for given number of ticks */
{
- pcppi_bell(ppicookie, xhz, ticks, PCPPI_BELL_SLEEP);
+ audiobell(vaudiocookie, xhz, ticks * (1000 / hz), 80, 0);
}
static void
@@ -129,7 +151,7 @@
printf("rest: %d\n", ticks);
#endif /* SPKRDEBUG */
if (ticks > 0)
- tsleep(rest, SPKRPRI | PCATCH, "rest", ticks);
+ audiobell(vaudiocookie, 0, ticks * (1000 / hz), 80, 0);
}
/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
@@ -424,14 +446,30 @@
return (!spkr_attached);
}
+device_t
+speakerattach_mi(device_t dev)
+{
+ struct spkr_attach_args sa;
+ sa.dev = dev;
+ return config_found(dev, &sa, NULL);
+}
+
void
spkrattach(device_t parent, device_t self, void *aux)
{
+ struct spkr_attach_args *sa;
+ sa = aux;
+
printf("\n");
- ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
+ vacookie = sa->dev;
+ vaudiocookie = &vacookie;
spkr_attached = 1;
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
+ mutex_init(&sc_bellock, MUTEX_DEFAULT, IPL_SCHED);
+ cv_init(&sc_bellcv, "bellcv");
+ kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, bell_thread, &sc_bell_args,
+ &sc_bellthread, "vbell");
}
int
@@ -439,8 +477,19 @@
{
pmf_device_deregister(self);
+
+ mutex_enter(&sc_bellock);
+ sc_bell_args.dying = true;
+
+ cv_broadcast(&sc_bellcv);
+ mutex_exit(&sc_bellock);
+
+ kthread_join(sc_bellthread);
+ cv_destroy(&sc_bellcv);
+ mutex_destroy(&sc_bellock);
+
spkr_attached = 0;
- ppicookie = NULL;
+ vaudiocookie = NULL;
return 0;
}
@@ -546,6 +595,44 @@
return(0);
}
+void
+bell_thread(void *arg)
+{
+ struct vbell_args *vb = arg;
+ u_int bpitch;
+ u_int bperiod;
+ u_int bvolume;
+
+ for (;;) {
+ mutex_enter(&sc_bellock);
+ cv_wait_sig(&sc_bellcv, &sc_bellock);
+
+ if (vb->dying == true) {
+ mutex_exit(&sc_bellock);
+ kthread_exit(0);
+ }
+
+ bpitch = vb->pitch;
+ bperiod = vb->period;
+ bvolume = vb->volume;
+ mutex_exit(&sc_bellock);
+ audiobell(vaudiocookie, bpitch, bperiod, bvolume, 0);
+ }
+}
+
+void
+speaker_play(u_int pitch, u_int period, u_int volume)
+{
+ mutex_enter(&sc_bellock);
+ sc_bell_args.dying = false;
+ sc_bell_args.pitch = pitch;
+ sc_bell_args.period = period;
+ sc_bell_args.volume = volume;
+
+ cv_broadcast(&sc_bellcv);
+ mutex_exit(&sc_bellock);
+}
+
static int
spkr_modcmd(modcmd_t cmd, void *arg)
{
Index: src/sys/dev/wscons/wskbd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wskbd.c,v
retrieving revision 1.136
diff -u -r1.136 wskbd.c
--- src/sys/dev/wscons/wskbd.c 24 Aug 2015 22:50:33 -0000 1.136
+++ src/sys/dev/wscons/wskbd.c 21 Apr 2016 12:21:30 -0000
@@ -156,6 +156,10 @@
#include <dev/wscons/wsmuxvar.h>
+#ifdef VAUDIO
+#include <dev/spkrvar.h>
+#endif
+
struct wskbd_internal {
const struct wskbd_mapdata *t_keymap;
@@ -1089,16 +1093,27 @@
case WSKBDIO_BELL:
if ((flag & FWRITE) == 0)
return (EACCES);
+#ifndef VAUDIO
return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
WSKBDIO_COMPLEXBELL, (void *)&sc->sc_bell_data, flag, l));
+#else
+ wskbd_cnbell(0, sc->sc_bell_data.pitch, sc->sc_bell_data.period,
+ sc->sc_bell_data.volume);
+ return 0;
+#endif
case WSKBDIO_COMPLEXBELL:
if ((flag & FWRITE) == 0)
return (EACCES);
ubdp = (struct wskbd_bell_data *)data;
SETBELL(ubdp, ubdp, &sc->sc_bell_data);
+#ifndef VAUDIO
return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
WSKBDIO_COMPLEXBELL, (void *)ubdp, flag, l));
+#else
+ wskbd_cnbell(0, ubdp->pitch, ubdp->period, ubdp->volume);
+ return 0;
+#endif
case WSKBDIO_SETBELL:
if ((flag & FWRITE) == 0)
@@ -1466,10 +1481,14 @@
if (!wskbd_console_initted)
return;
+#ifndef VAUDIO
if (wskbd_console_data.t_consops->bell != NULL)
(*wskbd_console_data.t_consops->bell)
(wskbd_console_data.t_consaccesscookie, pitch, period,
volume);
+#else
+ speaker_play(pitch, period, volume);
+#endif
}
Index: src/sys/arch/amd64/conf/majors.amd64
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/majors.amd64,v
retrieving revision 1.25
diff -u -r1.25 majors.amd64
--- src/sys/arch/amd64/conf/majors.amd64 23 Apr 2015 23:22:51 -0000 1.25
+++ src/sys/arch/amd64/conf/majors.amd64 21 Apr 2016 12:24:11 -0000
@@ -29,8 +29,6 @@
device-major bpf char 23 bpfilter
device-major md char 24 block 17 md
-device-major spkr char 27 spkr
-
device-major cy char 38 cy
device-major mcd char 39 block 7 mcd
device-major tun char 40 tun
Index: src/sys/dev/isa/files.isa
===================================================================
RCS file: /cvsroot/src/sys/dev/isa/files.isa,v
retrieving revision 1.163
diff -u -r1.163 files.isa
--- src/sys/dev/isa/files.isa 10 Jun 2013 07:14:02 -0000 1.163
+++ src/sys/dev/isa/files.isa 25 May 2016 12:26:30 -0000
@@ -432,9 +432,6 @@
device pcppi {}
attach pcppi at isa
file dev/isa/pcppi.c pcppi needs-flag
-device spkr
-attach spkr at pcppi
-file dev/isa/spkr.c spkr needs-flag
attach midi at pcppi with midi_pcppi: midisyn
file dev/isa/midi_pcppi.c midi_pcppi
Index: audiovar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/audiovar.h,v
retrieving revision 1.46
diff -u -r1.46 audiovar.h
--- src/sys/dev/audiovar.h 23 Nov 2011 23:07:31 -0000 1.46
+++ src/sys/dev/audiovar.h 3 Jun 2016 22:46:23 -0000
@@ -67,6 +67,7 @@
#define _SYS_DEV_AUDIOVAR_H_
#include <sys/condvar.h>
+#include <sys/proc.h>
#include <dev/audio_if.h>
@@ -101,6 +102,35 @@
bool mmapped; /* device is mmap()-ed */
};
+#ifndef VAUDIOCHANS
+#define VAUDIOCHANS 4096
+#endif
+
+struct virtual_channel {
+ u_char sc_open; /* multiple use device */
+ u_char sc_mode; /* bitmask for RECORD/PLAY */
+
+ bool sc_blkset; /* Blocksize has been set */
+
+ uint8_t *sc_sil_start; /* start of silence in buffer */
+ int sc_sil_count; /* # of silence bytes */
+ bool sc_pbus; /* output DMA in progress */
+ audio_params_t sc_pparams; /* play encoding parameters */
+ audio_stream_t *sc_pustream; /* the first buffer */
+ int sc_npfilters; /* number of filters */
+ audio_stream_t sc_pstreams[AUDIO_MAX_FILTERS];
+ stream_filter_t *sc_pfilters[AUDIO_MAX_FILTERS];
+ struct audio_ringbuffer sc_mpr; /* Play ring to mix*/
+ u_long sc_wstamp; /* # of bytes read with read(2) */
+ u_long sc_playdrop;
+
+ int sc_full_duplex; /* device in full duplex mode */
+
+ struct audio_info sc_lastinfo;
+ bool sc_lastinfovalid;
+ uint8_t sc_swvol;
+};
+
#define AUDIO_N_PORTS 4
struct au_mixer_ports {
@@ -126,16 +156,17 @@
void *hw_hdl; /* Hardware driver handle */
const struct audio_hw_if *hw_if; /* Hardware interface */
device_t sc_dev; /* Hardware device struct */
- u_char sc_open; /* single use device */
+ pid_t sc_audiopid[VAUDIOCHANS]; /* audio caller */
#define AUOPEN_READ 0x01
#define AUOPEN_WRITE 0x02
- u_char sc_mode; /* bitmask for RECORD/PLAY */
+ struct audio_encoding_set *sc_encodings;
struct selinfo sc_wsel; /* write selector */
struct selinfo sc_rsel; /* read selector */
pid_t sc_async_audio; /* process who wants audio SIGIO */
void *sc_sih_rd;
void *sc_sih_wr;
+ struct virtual_channel *sc_vchan[VAUDIOCHANS];
struct mixer_asyncs {
struct mixer_asyncs *next;
pid_t pid;
@@ -147,16 +178,12 @@
kcondvar_t sc_rchan;
kcondvar_t sc_wchan;
kcondvar_t sc_lchan;
- int sc_dvlock;
+ bool sc_trigger_started;
+ bool sc_writeme;
+ int sc_opens;
bool sc_dying;
- bool sc_blkset; /* Blocksize has been set */
-
- uint8_t *sc_sil_start; /* start of silence in buffer */
- int sc_sil_count; /* # of silence bytes */
-
bool sc_rbus; /* input DMA in progress */
- bool sc_pbus; /* output DMA in progress */
/**
* userland
@@ -168,15 +195,19 @@
* sc_pstreams[n-1] <list_t::filters[1].param>
* | sc_pfilters[n-1]
* sc_pr <list_t::filters[0].param>
+ * (vchans mixed into sc_pr)
+ *
+ * play_thread
+ * sc_pr
* |
+ * vchan[0]->sc_pustream
+ * |
+ * vchan[0]->sc_mpr
+ * |
* hardware
*/
- audio_params_t sc_pparams; /* play encoding parameters */
- audio_stream_t *sc_pustream; /* the first buffer */
- int sc_npfilters; /* number of filters */
- audio_stream_t sc_pstreams[AUDIO_MAX_FILTERS];
- stream_filter_t *sc_pfilters[AUDIO_MAX_FILTERS];
- struct audio_ringbuffer sc_pr; /* Play ring */
+
+ struct audio_ringbuffer sc_pr; /* Play ring to mix into */
/**
* hardware
@@ -200,11 +231,6 @@
audio_params_t sc_rparams; /* record encoding parameters */
int sc_eof; /* EOF, i.e. zero sized write, counter */
- u_long sc_wstamp; /* # of bytes read with read(2) */
- u_long sc_playdrop;
-
- int sc_full_duplex; /* device in full duplex mode */
-
struct au_mixer_ports sc_inports, sc_outports;
int sc_monitor_port;
@@ -220,11 +246,16 @@
#endif
u_int sc_lastgain;
- struct audio_info sc_lastinfo;
- bool sc_lastinfovalid;
mixer_ctrl_t *sc_mixer_state;
int sc_nmixer_states;
+ int sc_static_nmixer_states;
+
+ bool schedule_wih;
+ bool schedule_rih;
+
+ lwp_t *sc_playthread;
+ kcondvar_t sc_condvar;
};
#endif /* _SYS_DEV_AUDIOVAR_H_ */
Index: audio.c
===================================================================
RCS file: /cvsroot/src/sys/dev/audio.c,v
retrieving revision 1.267
diff -u -r1.267 audio.c
--- src/sys/dev/audio.c 23 Apr 2016 10:15:31 -0000 1.267
+++ src/sys/dev/audio.c 3 Jun 2016 22:46:47 -0000
@@ -1,6 +1,32 @@
/* $NetBSD: audio.c,v 1.267 2016/04/23 10:15:31 skrll Exp $ */
/*-
+ * Copyright (c) 2016 Nathanial Sloss <
[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -109,16 +135,6 @@
* sc_intr_lock. This is to ensure that groups of hardware operations are
* made atomically. SLEEPS CANNOT OCCUR WITH THIS LOCK HELD.
*
- * - sc_dvlock, private to this module. This is a custom reader/writer lock
- * built on sc_lock and a condition variable. Some operations release
- * sc_lock in order to allocate memory, to wait for in-flight I/O to
- * complete, to copy to/from user context, etc. sc_dvlock serializes
- * changes to filters and audio device settings while a read/write to the
- * hardware is in progress. A write lock is taken only under exceptional
- * circumstances, for example when opening /dev/audio or changing audio
- * parameters. Long term sleeps and copy to/from user space may be done
- * with this lock held.
- *
* List of hardware interface methods, and which locks are held when each
* is called by this module:
*
@@ -160,6 +176,7 @@
#include "audio.h"
#if NAUDIO > 0
+#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
@@ -177,10 +194,17 @@
#include <sys/audioio.h>
#include <sys/device.h>
#include <sys/intr.h>
+#include <sys/kthread.h>
#include <sys/cpu.h>
#include <dev/audio_if.h>
#include <dev/audiovar.h>
+#include <dev/auconv.h>
+#include <dev/auvolconv.h>
+
+#ifdef VAUDIOSPEAKER
+#include <dev/spkrvar.h>
+#endif
#include <machine/endian.h>
@@ -198,6 +222,12 @@
#define SPECIFIED(x) (x != ~0)
#define SPECIFIED_CH(x) (x != (u_char)~0)
+#define VAUDIO_NFORMATS 1
+static const struct audio_format vaudio_formats[VAUDIO_NFORMATS] = {
+ { NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
+ 2, AUFMT_STEREO, 1, { 44100 } },
+};
+
/* #define AUDIO_PM_IDLE */
#ifdef AUDIO_PM_IDLE
int audio_idle_timeout = 30;
@@ -205,11 +235,11 @@
int audio_blk_ms = AUDIO_BLK_MS;
-int audiosetinfo(struct audio_softc *, struct audio_info *);
-int audiogetinfo(struct audio_softc *, struct audio_info *, int);
+int audiosetinfo(struct audio_softc *, struct audio_info *, bool, int);
+int audiogetinfo(struct audio_softc *, struct audio_info *, int, int);
int audio_open(dev_t, struct audio_softc *, int, int, struct lwp *);
-int audio_close(struct audio_softc *, int, int, struct lwp *);
+int audio_close(struct audio_softc *, int, int, struct lwp *, int);
int audio_read(struct audio_softc *, struct uio *, int);
int audio_write(struct audio_softc *, struct uio *, int);
int audio_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *);
@@ -223,33 +253,37 @@
static void mixer_remove(struct audio_softc *);
static void mixer_signal(struct audio_softc *);
-void audio_init_record(struct audio_softc *);
-void audio_init_play(struct audio_softc *);
+void audio_init_record(struct audio_softc *, int);
+void audio_init_play(struct audio_softc *, int);
int audiostartr(struct audio_softc *);
-int audiostartp(struct audio_softc *);
+int audiostartp(struct audio_softc *, int);
void audio_rint(void *);
void audio_pint(void *);
+void audio_mix(void *);
+void audio_play_thread(void *);
+void mix_func(struct audio_softc *, struct audio_ringbuffer *, int);
+void mix_write(void *);
int audio_check_params(struct audio_params *);
-void audio_calc_blksize(struct audio_softc *, int);
+void audio_calc_blksize(struct audio_softc *, int, int);
void audio_fill_silence(struct audio_params *, uint8_t *, int);
int audio_silence_copyout(struct audio_softc *, int, struct uio *);
void audio_init_ringbuffer(struct audio_softc *,
struct audio_ringbuffer *, int);
-int audio_initbufs(struct audio_softc *);
-void audio_calcwater(struct audio_softc *);
-int audio_drain(struct audio_softc *);
-void audio_clear(struct audio_softc *);
-void audio_clear_intr_unlocked(struct audio_softc *sc);
+int audio_initbufs(struct audio_softc *, int);
+void audio_calcwater(struct audio_softc *, int);
+int audio_drain(struct audio_softc *, int);
+void audio_clear(struct audio_softc *, int);
+void audio_clear_intr_unlocked(struct audio_softc *sc, int);
static inline void audio_pint_silence
- (struct audio_softc *, struct audio_ringbuffer *, uint8_t *, int);
+ (struct audio_softc *, struct audio_ringbuffer *, uint8_t *, int, int);
int audio_alloc_ring
(struct audio_softc *, struct audio_ringbuffer *, int, size_t);
void audio_free_ring(struct audio_softc *, struct audio_ringbuffer *);
static int audio_setup_pfilters(struct audio_softc *, const audio_params_t *,
- stream_filter_list_t *);
+ stream_filter_list_t *, int);
static int audio_setup_rfilters(struct audio_softc *, const audio_params_t *,
stream_filter_list_t *);
static void audio_stream_dtor(audio_stream_t *);
@@ -263,7 +297,7 @@
static void stream_filter_list_set
(stream_filter_list_t *, int, stream_filter_factory_t,
const audio_params_t *);
-int audio_set_defaults(struct audio_softc *, u_int);
+int audio_set_defaults(struct audio_softc *, u_int, int);
int audioprobe(device_t, cfdata_t, void *);
void audioattach(device_t, device_t, void *);
@@ -295,6 +329,8 @@
static void audio_exit(struct audio_softc *);
static int audio_waitio(struct audio_softc *, kcondvar_t *);
+#define AUDIO_OUTPUT_CLASS 0
+
struct portname {
const char *name;
int mask;
@@ -320,6 +356,20 @@
int au_set_port(struct audio_softc *, struct au_mixer_ports *,
u_int);
int au_get_port(struct audio_softc *, struct au_mixer_ports *);
+static int
+ audio_get_port(struct audio_softc *, mixer_ctrl_t *);
+static int
+ audio_set_port(struct audio_softc *, mixer_ctrl_t *);
+static int
+ audio_query_devinfo(struct audio_softc *, mixer_devinfo_t *);
+static int
+audio_set_params(struct audio_softc *, int, int, audio_params_t *,
+ audio_params_t *, stream_filter_list_t *, stream_filter_list_t *, int);
+static int
+audio_query_encoding(struct audio_softc *, struct audio_encoding *);
+static int
+audio_set_vchan_defaults(struct audio_softc *, u_int,
+ const struct audio_format *, int);
int au_get_lr_value(struct audio_softc *, mixer_ctrl_t *,
int *, int *);
int au_set_lr_value(struct audio_softc *, mixer_ctrl_t *,
@@ -360,7 +410,16 @@
.d_mmap = audiommap,
.d_kqfilter = audiokqfilter,
.d_discard = nodiscard,
- .d_flag = D_OTHER | D_MPSAFE
+ .d_flag = D_MCLOSE | D_MPSAFE
+};
+
+/* The default vchan mode: 48 kHz stereo signed linear */
+struct audio_params vchan_default = {
+ .sample_rate = 44100,
+ .encoding = AUDIO_ENCODING_SLINEAR_LE,
+ .precision = 16,
+ .validbits = 16,
+ .channels = 2,
};
/* The default audio mode: 8 kHz mono mu-law */
@@ -394,6 +453,7 @@
{
struct audio_softc *sc;
struct audio_attach_args *sa;
+ struct virtual_channel *vc;
const struct audio_hw_if *hwp;
void *hdlp;
int error;
@@ -407,10 +467,30 @@
sa = aux;
hwp = sa->hwif;
hdlp = sa->hdl;
+ sc->sc_opens = 0;
+
+ sc->sc_trigger_started = false;
+ sc->sc_vchan[0] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
+ vc = sc->sc_vchan[0];
+ memset(sc->sc_audiopid, -1, sizeof(sc->sc_audiopid));
+ vc->sc_open = 0;
+ vc->sc_mode = 0;
+ vc->sc_npfilters = 0;
+ memset(vc->sc_pfilters, 0,
+ sizeof(vc->sc_pfilters));
+ vc->sc_lastinfovalid = false;
+ vc->sc_swvol = 255;
+
+ if (auconv_create_encodings(vaudio_formats, VAUDIO_NFORMATS,
+ &sc->sc_encodings) != 0) {
+ aprint_error_dev(self, "couldn't create encodings\n");
+ return;
+ }
cv_init(&sc->sc_rchan, "audiord");
cv_init(&sc->sc_wchan, "audiowr");
cv_init(&sc->sc_lchan, "audiolk");
+ cv_init(&sc->sc_condvar,"play");
if (hwp == 0 || hwp->get_locks == 0) {
printf(": missing method\n");
@@ -440,7 +520,6 @@
sc->hw_if = hwp;
sc->hw_hdl = hdlp;
sc->sc_dev = parent;
- sc->sc_lastinfovalid = false;
mutex_enter(sc->sc_lock);
props = audio_get_props(sc);
@@ -466,11 +545,12 @@
mutex_enter(sc->sc_lock);
can_playback = audio_can_playback(sc);
can_capture = audio_can_capture(sc);
- mutex_exit(sc->sc_lock);
if (can_playback) {
error = audio_alloc_ring(sc, &sc->sc_pr,
- AUMODE_PLAY, AU_RING_SIZE);
+ AUMODE_PLAY, AU_RING_SIZE);
+ error = audio_alloc_ring(sc, &sc->sc_vchan[0]->sc_mpr,
+ AUMODE_PLAY, AU_RING_SIZE);
if (error) {
sc->hw_if = NULL;
aprint_error("audio: could not allocate play buffer\n");
@@ -483,6 +563,8 @@
if (error) {
if (sc->sc_pr.s.start != 0)
audio_free_ring(sc, &sc->sc_pr);
+ if (sc->sc_vchan[0]->sc_mpr.s.start != 0)
+ audio_free_ring(sc, &sc->sc_vchan[0]->sc_mpr);
sc->hw_if = NULL;
aprint_error("audio: could not allocate record buffer\n");
return;
@@ -491,8 +573,8 @@
sc->sc_lastgain = 128;
- mutex_enter(sc->sc_lock);
- error = audio_set_defaults(sc, 0);
+ audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL | AUMODE_RECORD,
+ &vaudio_formats[0], 0);
mutex_exit(sc->sc_lock);
if (error != 0) {
aprint_error("audioattach: audio_set_defaults() failed\n");
@@ -500,6 +582,7 @@
return;
}
+ sc->sc_pr.blksize = sc->sc_vchan[0]->sc_mpr.blksize;
sc->sc_sih_rd = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE,
audio_softintr_rd, sc);
sc->sc_sih_wr = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE,
@@ -530,7 +613,7 @@
*/
mutex_enter(sc->sc_lock);
for(mi.index = 0; ; mi.index++) {
- if (hwp->query_devinfo(hdlp, &mi) != 0)
+ if (audio_query_devinfo(sc, &mi) != 0)
break;
/*
* The type of AUDIO_MIXER_CLASS merely introduces a class.
@@ -551,8 +634,9 @@
/* Allocate save area. Ensure non-zero allocation. */
sc->sc_nmixer_states = mi.index;
- sc->sc_mixer_state = kmem_alloc(sizeof(mixer_ctrl_t) *
- sc->sc_nmixer_states + 1, KM_SLEEP);
+ sc->sc_static_nmixer_states = mi.index;
+ sc->sc_mixer_state = kmem_zalloc(sizeof(mixer_ctrl_t) *
+ (sc->sc_nmixer_states + VAUDIOCHANS + 1), KM_SLEEP);
/*
* This is where we assign each control in the "audio" model, to the
@@ -563,7 +647,7 @@
record_source_found = 0;
mutex_enter(sc->sc_lock);
for(mi.index = 0; ; mi.index++) {
- if (hwp->query_devinfo(hdlp, &mi) != 0)
+ if (audio_query_devinfo(sc, &mi) != 0)
break;
KASSERT(mi.index < sc->sc_nmixer_states);
if (mi.type == AUDIO_MIXER_CLASS)
@@ -670,6 +754,11 @@
#ifdef AUDIO_PM_IDLE
callout_schedule(&sc->sc_idle_counter, audio_idle_timeout * hz);
#endif
+ kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
+ audio_play_thread, sc, &sc->sc_playthread, "audiomix");
+#ifdef VAUDIOSPEAKER
+ speakerattach_mi(self);
+#endif
}
int
@@ -692,16 +781,23 @@
audiodetach(device_t self, int flags)
{
struct audio_softc *sc;
- int maj, mn, i;
+ int maj, mn, i, n, rc;
sc = device_private(self);
DPRINTF(("audio_detach: sc=%p flags=%d\n", sc, flags));
/* Start draining existing accessors of the device. */
+ if ((rc = config_detach_children(self, flags)) != 0)
+ return rc;
mutex_enter(sc->sc_lock);
sc->sc_dying = true;
cv_broadcast(&sc->sc_wchan);
cv_broadcast(&sc->sc_rchan);
+ cv_broadcast(&sc->sc_condvar);
+ mutex_exit(sc->sc_lock);
+ kthread_join(sc->sc_playthread);
+ mutex_enter(sc->sc_lock);
+ cv_destroy(&sc->sc_condvar);
mutex_exit(sc->sc_lock);
/* locate the major number */
@@ -737,6 +833,11 @@
pmf_device_deregister(self);
/* free resources */
+ for (n = 0; n < VAUDIOCHANS; n++) {
+ if (n != 0 && sc->sc_audiopid[n] == -1)
+ continue;
+ audio_free_ring(sc, &sc->sc_vchan[n]->sc_mpr);
+ }
audio_free_ring(sc, &sc->sc_pr);
audio_free_ring(sc, &sc->sc_rr);
for (i = 0; i < sc->sc_nrfilters; i++) {
@@ -745,12 +846,18 @@
audio_stream_dtor(&sc->sc_rstreams[i]);
}
sc->sc_nrfilters = 0;
- for (i = 0; i < sc->sc_npfilters; i++) {
- sc->sc_pfilters[i]->dtor(sc->sc_pfilters[i]);
- sc->sc_pfilters[i] = NULL;
- audio_stream_dtor(&sc->sc_pstreams[i]);
+ for (n = 0; n < VAUDIOCHANS; n++) {
+ if (n != 0 && sc->sc_audiopid[n] == -1)
+ continue;
+ for (i = 0; i < sc->sc_vchan[n]->sc_npfilters; i++) {
+ sc->sc_vchan[n]->sc_pfilters[i]->dtor(sc->sc_vchan[n]->sc_pfilters[i]);
+ sc->sc_vchan[n]->sc_pfilters[i] = NULL;
+ audio_stream_dtor(&sc->sc_vchan[n]->sc_pstreams[i]);
+ }
+ sc->sc_vchan[n]->sc_npfilters = 0;
}
- sc->sc_npfilters = 0;
+
+ auconv_delete_encodings(sc->sc_encodings);
if (sc->sc_sih_rd) {
softint_disestablish(sc->sc_sih_rd);
@@ -761,6 +868,8 @@
sc->sc_sih_wr = NULL;
}
+ kmem_free(sc->sc_vchan[0], sizeof(struct virtual_channel));
+
#ifdef AUDIO_PM_IDLE
callout_destroy(&sc->sc_idle_counter);
#endif
@@ -780,7 +889,7 @@
mixer_devinfo_t mi;
for(mi.index = 0;
- sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0;
+ audio_query_devinfo(sc, &mi) == 0;
mi.index++)
if (mi.mixer_class == class && strcmp(mi.label.name, name) == 0)
return mi.index;
@@ -857,16 +966,28 @@
void
audio_printsc(struct audio_softc *sc)
{
+ int n;
+
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+
+ if (n == VAUDIOCHANS)
+ return;
+
printf("hwhandle %p hw_if %p ", sc->hw_hdl, sc->hw_if);
- printf("open 0x%x mode 0x%x\n", sc->sc_open, sc->sc_mode);
+ printf("open 0x%x mode 0x%x\n", sc->sc_vchan[n]->sc_open,
+ sc->sc_vchan[n]->sc_mode);
printf("rchan 0x%x wchan 0x%x ", cv_has_waiters(&sc->sc_rchan),
cv_has_waiters(&sc->sc_wchan));
printf("rring used 0x%x pring used=%d\n",
audio_stream_get_used(&sc->sc_rr.s),
- audio_stream_get_used(&sc->sc_pr.s));
- printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_pbus);
- printf("blksize %d", sc->sc_pr.blksize);
- printf("hiwat %d lowat %d\n", sc->sc_pr.usedhigh, sc->sc_pr.usedlow);
+ audio_stream_get_used(&sc->sc_vhan[n].sc_mpr.s));
+ printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_vchan[n]->sc_pbus);
+ printf("blksize %d", sc->sc_vchan[n]->sc_mpr.blksize);
+ printf("hiwat %d lowat %d\n", sc->sc_vchan[n]->sc_mpr.usedhigh,
+ sc->sc_vchan[n]->sc_mpr.usedlow);
}
void
@@ -893,17 +1014,16 @@
bufsize = AUMINBUF;
ROUNDSIZE(bufsize);
if (hw->round_buffersize) {
- mutex_enter(sc->sc_lock);
bufsize = hw->round_buffersize(hdl, direction, bufsize);
- mutex_exit(sc->sc_lock);
}
- if (hw->allocm)
+ if (hw->allocm && (r == &sc->sc_vchan[0]->sc_mpr || r == &sc->sc_rr))
r->s.start = hw->allocm(hdl, direction, bufsize);
else
- r->s.start = kmem_alloc(bufsize, KM_SLEEP);
+ r->s.start = kmem_zalloc(bufsize, KM_SLEEP);
if (r->s.start == 0)
return ENOMEM;
r->s.bufsize = bufsize;
+
return 0;
}
@@ -913,7 +1033,7 @@
if (r->s.start == 0)
return;
- if (sc->hw_if->freem)
+ if (sc->hw_if->freem && (r == &sc->sc_vchan[0]->sc_mpr || r == &sc->sc_rr))
sc->hw_if->freem(sc->hw_hdl, r->s.start, r->s.bufsize);
else
kmem_free(r->s.start, r->s.bufsize);
@@ -922,7 +1042,7 @@
static int
audio_setup_pfilters(struct audio_softc *sc, const audio_params_t *pp,
- stream_filter_list_t *pfilters)
+ stream_filter_list_t *pfilters, int m)
{
stream_filter_t *pf[AUDIO_MAX_FILTERS], *of[AUDIO_MAX_FILTERS];
audio_stream_t ps[AUDIO_MAX_FILTERS], os[AUDIO_MAX_FILTERS];
@@ -964,22 +1084,22 @@
/* Swap in new filters. */
mutex_enter(sc->sc_intr_lock);
- memcpy(of, sc->sc_pfilters, sizeof(of));
- memcpy(os, sc->sc_pstreams, sizeof(os));
- onfilters = sc->sc_npfilters;
- memcpy(sc->sc_pfilters, pf, sizeof(pf));
- memcpy(sc->sc_pstreams, ps, sizeof(ps));
- sc->sc_npfilters = pfilters->req_size;
+ memcpy(of, sc->sc_vchan[m]->sc_pfilters, sizeof(of));
+ memcpy(os, sc->sc_vchan[m]->sc_pstreams, sizeof(os));
+ onfilters = sc->sc_vchan[m]->sc_npfilters;
+ memcpy(sc->sc_vchan[m]->sc_pfilters, pf, sizeof(pf));
+ memcpy(sc->sc_vchan[m]->sc_pstreams, ps, sizeof(ps));
+ sc->sc_vchan[m]->sc_npfilters = pfilters->req_size;
for (i = 0; i < pfilters->req_size; i++) {
- pf[i]->set_inputbuffer(pf[i], &sc->sc_pstreams[i]);
+ pf[i]->set_inputbuffer(pf[i], &sc->sc_vchan[m]->sc_pstreams[i]);
}
/* hardware format and the buffer near to userland */
if (pfilters->req_size <= 0) {
- sc->sc_pr.s.param = *pp;
- sc->sc_pustream = &sc->sc_pr.s;
+ sc->sc_vchan[m]->sc_mpr.s.param = *pp;
+ sc->sc_vchan[m]->sc_pustream = &sc->sc_vchan[m]->sc_mpr.s;
} else {
- sc->sc_pr.s.param = pfilters->filters[0].param;
- sc->sc_pustream = &sc->sc_pstreams[0];
+ sc->sc_vchan[m]->sc_mpr.s.param = pfilters->filters[0].param;
+ sc->sc_vchan[m]->sc_pustream = &sc->sc_vchan[m]->sc_pstreams[0];
}
mutex_exit(sc->sc_intr_lock);
@@ -993,13 +1113,13 @@
#ifdef AUDIO_DEBUG
printf("%s: HW-buffer=%p pustream=%p\n",
- __func__, &sc->sc_pr.s, sc->sc_pustream);
+ __func__, &sc->sc_vchan[m]->sc_mpr.s, sc->sc_vchan[m]->sc_pustream);
for (i = 0; i < pfilters->req_size; i++) {
char num[100];
snprintf(num, 100, "[%d]", i);
- audio_print_params(num, &sc->sc_pstreams[i].param);
+ audio_print_params(num, &sc->sc_vchan[m]->sc_pstreams[i].param);
}
- audio_print_params("[HW]", &sc->sc_pr.s.param);
+ audio_print_params("[HW]", &sc->sc_vchan[m]->sc_mpr.s.param);
#endif /* AUDIO_DEBUG */
return 0;
@@ -1111,7 +1231,7 @@
size = min(size, AU_RING_SIZE);
stream->bufsize = size;
- stream->start = kmem_alloc(size, KM_SLEEP);
+ stream->start = kmem_zalloc(size, KM_SLEEP);
if (stream->start == NULL)
return ENOMEM;
frame_size = (param->precision + 7) / 8 * param->channels;
@@ -1182,6 +1302,7 @@
static int
audio_enter(dev_t dev, krw_t rw, struct audio_softc **scp)
{
+
struct audio_softc *sc;
/* First, find the device and take sc_lock. */
@@ -1194,24 +1315,6 @@
return EIO;
}
- /* Acquire device access lock. */
- switch (rw) {
- case RW_WRITER:
- while (__predict_false(sc->sc_dvlock != 0)) {
- cv_wait(&sc->sc_lchan, sc->sc_lock);
- }
- sc->sc_dvlock = -1;
- break;
- case RW_READER:
- while (__predict_false(sc->sc_dvlock < 0)) {
- cv_wait(&sc->sc_lchan, sc->sc_lock);
- }
- sc->sc_dvlock++;
- break;
- default:
- panic("audio_enter");
- }
-
*scp = sc;
return 0;
}
@@ -1222,16 +1325,6 @@
static void
audio_exit(struct audio_softc *sc)
{
-
- KASSERT(mutex_owned(sc->sc_lock));
- KASSERT(sc->sc_dvlock != 0);
-
- /* Release device level lock. */
- if (__predict_false(sc->sc_dvlock < 0)) {
- sc->sc_dvlock = 0;
- } else {
- sc->sc_dvlock--;
- }
cv_broadcast(&sc->sc_lchan);
mutex_exit(sc->sc_lock);
}
@@ -1243,37 +1336,13 @@
audio_waitio(struct audio_softc *sc, kcondvar_t *chan)
{
int error;
- krw_t rw;
KASSERT(mutex_owned(sc->sc_lock));
-
- /* Release device level lock while sleeping. */
- if (__predict_false(sc->sc_dvlock < 0)) {
- sc->sc_dvlock = 0;
- rw = RW_WRITER;
- } else {
- KASSERT(sc->sc_dvlock > 0);
- sc->sc_dvlock--;
- rw = RW_READER;
- }
cv_broadcast(&sc->sc_lchan);
/* Wait for pending I/O to complete. */
error = cv_wait_sig(chan, sc->sc_lock);
- /* Re-acquire device level lock. */
- if (__predict_false(rw == RW_WRITER)) {
- while (__predict_false(sc->sc_dvlock != 0)) {
- cv_wait(&sc->sc_lchan, sc->sc_lock);
- }
- sc->sc_dvlock = -1;
- } else {
- while (__predict_false(sc->sc_dvlock < 0)) {
- cv_wait(&sc->sc_lchan, sc->sc_lock);
- }
- sc->sc_dvlock++;
- }
-
return error;
}
@@ -1310,7 +1379,7 @@
audioclose(dev_t dev, int flags, int ifmt, struct lwp *l)
{
struct audio_softc *sc;
- int error;
+ int error, n;
if ((error = audio_enter(dev, RW_WRITER, &sc)) != 0)
return error;
@@ -1318,7 +1387,15 @@
switch (AUDIODEV(dev)) {
case SOUND_DEVICE:
case AUDIO_DEVICE:
- error = audio_close(sc, flags, ifmt, l);
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS) {
+ error = EIO;
+ break;
+ }
+ error = audio_close(sc, flags, ifmt, l, n);
break;
case MIXER_DEVICE:
error = mixer_close(sc, flags, ifmt, l);
@@ -1565,19 +1642,23 @@
rp->copying = false;
rp->needfill = false;
rp->mmapped = false;
+ memset(rp->s.start, 0, blksize * 2);
}
int
-audio_initbufs(struct audio_softc *sc)
+audio_initbufs(struct audio_softc *sc, int n)
{
const struct audio_hw_if *hw;
+ struct virtual_channel *vc;
int error;
- DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_mode));
+ DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_vchan[n]->sc_mode));
+ vc = sc->sc_vchan[0];
hw = sc->hw_if;
if (audio_can_capture(sc)) {
audio_init_ringbuffer(sc, &sc->sc_rr, AUMODE_RECORD);
- if (hw->init_input && (sc->sc_mode & AUMODE_RECORD)) {
+ if (sc->sc_opens == 1 && hw->init_input &&
+ (sc->sc_vchan[n]->sc_mode & AUMODE_RECORD)) {
error = hw->init_input(sc->hw_hdl, sc->sc_rr.s.start,
sc->sc_rr.s.end - sc->sc_rr.s.start);
if (error)
@@ -1586,11 +1667,12 @@
}
if (audio_can_playback(sc)) {
- audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
- sc->sc_sil_count = 0;
- if (hw->init_output && (sc->sc_mode & AUMODE_PLAY)) {
- error = hw->init_output(sc->hw_hdl, sc->sc_pr.s.start,
- sc->sc_pr.s.end - sc->sc_pr.s.start);
+ audio_init_ringbuffer(sc, &sc->sc_vchan[n]->sc_mpr, AUMODE_PLAY);
+ sc->sc_vchan[n]->sc_sil_count = 0;
+ if (sc->sc_opens == 1 && hw->init_output &&
+ (sc->sc_vchan[n]->sc_mode & AUMODE_PLAY)) {
+ error = hw->init_output(sc->hw_hdl, vc->sc_mpr.s.start,
+ vc->sc_mpr.s.end - vc->sc_mpr.s.start);
if (error)
return error;
}
@@ -1601,12 +1683,12 @@
if (audio_can_playback(sc)) {
sc->sc_pnintr = 0;
sc->sc_pblktime = (u_long)(
- (double)sc->sc_pr.blksize * 100000 /
- (double)(sc->sc_pparams.precision / NBBY *
- sc->sc_pparams.channels *
- sc->sc_pparams.sample_rate)) * 10;
+ (double)sc->sc_vchan[n]->sc_mpr.blksize * 100000 /
+ (double)(sc->sc_vchan[n]->sc_pparams.precision / NBBY *
+ sc->sc_vhan[n].sc_pparams.channels *
+ sc->sc_vhan[n].sc_pparams.sample_rate)) * 10;
DPRINTF(("audio: play blktime = %lu for %d\n",
- sc->sc_pblktime, sc->sc_pr.blksize));
+ sc->sc_pblktime, sc->sc_vhan[n].sc_mpr.blksize));
}
if (audio_can_capture(sc)) {
sc->sc_rnintr = 0;
@@ -1625,17 +1707,17 @@
}
void
-audio_calcwater(struct audio_softc *sc)
+audio_calcwater(struct audio_softc *sc, int n)
{
+ struct virtual_channel *vc = sc->sc_vchan[n];
/* set high at 100% */
if (audio_can_playback(sc)) {
- sc->sc_pr.usedhigh =
- sc->sc_pustream->end - sc->sc_pustream->start;
+ vc->sc_mpr.usedhigh = vc->sc_pustream->end - vc->sc_pustream->start;
/* set low at 75% of usedhigh */
- sc->sc_pr.usedlow = sc->sc_pr.usedhigh * 3 / 4;
- if (sc->sc_pr.usedlow == sc->sc_pr.usedhigh)
- sc->sc_pr.usedlow -= sc->sc_pr.blksize;
+ vc->sc_mpr.usedlow = vc->sc_mpr.usedhigh * 3 / 4;
+ if (vc->sc_mpr.usedlow == vc->sc_mpr.usedhigh)
+ vc->sc_mpr.usedlow -= vc->sc_mpr.blksize;
}
if (audio_can_capture(sc)) {
@@ -1644,7 +1726,7 @@
sc->sc_rr.blksize;
sc->sc_rr.usedlow = 0;
DPRINTF(("%s: plow=%d phigh=%d rlow=%d rhigh=%d\n", __func__,
- sc->sc_pr.usedlow, sc->sc_pr.usedhigh,
+ vc->sc_mpr.usedlow, vc->sc_mpr.usedhigh,
sc->sc_rr.usedlow, sc->sc_rr.usedhigh));
}
}
@@ -1653,51 +1735,106 @@
audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
struct lwp *l)
{
- int error;
+ int error, i, n;
u_int mode;
const struct audio_hw_if *hw;
+ struct virtual_channel *vc;
+ mixer_ctrl_t *mc;
KASSERT(mutex_owned(sc->sc_lock));
+ if (sc->sc_opens >= VAUDIOCHANS)
+ return ENXIO;
+
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n < VAUDIOCHANS)
+ return ENXIO;
+
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == -1)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return ENXIO;
+
hw = sc->hw_if;
if (hw == NULL)
return ENXIO;
+ sc->sc_vchan[n] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
+ if (sc->sc_vchan[n] == NULL)
+ return ENOMEM;
+ vc = sc->sc_vchan[n];
+
+ vc->sc_open = 0;
+ vc->sc_mode = 0;
+ vc->sc_sil_count = 0;
+ vc->sc_npfilters = 0;
+ memset(vc->sc_pfilters, 0,
+ sizeof(vc->sc_pfilters));
+ vc->sc_pbus = false;
+ vc->sc_blkset = false;
+ vc->sc_lastinfovalid = false;
+ vc->sc_swvol = 255;
+
DPRINTF(("audio_open: flags=0x%x sc=%p hdl=%p\n",
flags, sc, sc->hw_hdl));
- if (((flags & FREAD) && (sc->sc_open & AUOPEN_READ)) ||
- ((flags & FWRITE) && (sc->sc_open & AUOPEN_WRITE)))
+ if (((flags & FREAD) && (vc->sc_open & AUOPEN_READ)) ||
+ ((flags & FWRITE) && (vc->sc_open & AUOPEN_WRITE))) {
+ kmem_free(vc, sizeof(struct virtual_channel));
return EBUSY;
+ }
- if (hw->open != NULL) {
- mutex_enter(sc->sc_intr_lock);
- error = hw->open(sc->hw_hdl, flags);
- mutex_exit(sc->sc_intr_lock);
- if (error)
- return error;
+ sc->sc_opens++;
+ sc->sc_audiopid[n] = curproc->p_pid;
+ error = audio_alloc_ring(sc, &vc->sc_mpr,
+ AUMODE_PLAY, AU_RING_SIZE);
+ if (error) {
+ sc->sc_opens--;
+ sc->sc_audiopid[n] = -1;
+ kmem_free(vc, sizeof(struct virtual_channel));
+ return error;
}
- sc->sc_async_audio = 0;
- sc->sc_sil_count = 0;
- sc->sc_rbus = false;
- sc->sc_pbus = false;
- sc->sc_eof = 0;
- sc->sc_playdrop = 0;
+ if (sc->sc_opens == 1) {
+ if (hw->open != NULL) {
+ mutex_enter(sc->sc_intr_lock);
+ error = hw->open(sc->hw_hdl, flags);
+ mutex_exit(sc->sc_intr_lock);
+ if (error) {
+ sc->sc_opens--;
+ sc->sc_audiopid[n] = -1;
+ kmem_free(vc,
+ sizeof(struct virtual_channel));
+ return error;
+ }
+ }
+ audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
+ audio_initbufs(sc, 0);
+ sc->schedule_wih = false;
+ sc->schedule_rih = false;
+ sc->sc_eof = 0;
+ sc->sc_rbus = false;
+ sc->sc_async_audio = 0;
+ }
mutex_enter(sc->sc_intr_lock);
- sc->sc_full_duplex =
+ vc->sc_full_duplex =
(flags & (FWRITE|FREAD)) == (FWRITE|FREAD) &&
(audio_get_props(sc) & AUDIO_PROP_FULLDUPLEX);
mutex_exit(sc->sc_intr_lock);
mode = 0;
if (flags & FREAD) {
- sc->sc_open |= AUOPEN_READ;
+ vc->sc_open |= AUOPEN_READ;
mode |= AUMODE_RECORD;
}
if (flags & FWRITE) {
- sc->sc_open |= AUOPEN_WRITE;
+ vc->sc_open |= AUOPEN_WRITE;
mode |= AUMODE_PLAY | AUMODE_PLAY_ALL;
}
@@ -1706,14 +1843,13 @@
* The /dev/audio is always (re)set to 8-bit MU-Law mono
* For the other devices, you get what they were last set to.
*/
- if (ISDEVAUDIO(dev)) {
- error = audio_set_defaults(sc, mode);
- } else {
+ error = audio_set_defaults(sc, mode, n);
+ if (ISDEVSOUND(dev)) {
struct audio_info ai;
AUDIO_INITINFO(&ai);
ai.mode = mode;
- error = audiosetinfo(sc, &ai);
+ error = audiosetinfo(sc, &ai, true, n);
}
if (error)
goto bad;
@@ -1724,27 +1860,44 @@
* default values by the hardware driver, so that it may give
* us these values.
*/
- if (sc->sc_rparams.precision == 0 || sc->sc_pparams.precision == 0) {
+ if (sc->sc_rparams.precision == 0 || vc->sc_pparams.precision == 0) {
printf("audio_open: 0 precision\n");
- return EINVAL;
+ goto bad;
}
#endif
- /* audio_close() decreases sc_pr.usedlow, recalculate here */
- audio_calcwater(sc);
+ /* audio_close() decreases sc_mpr[n].usedlow, recalculate here */
+ audio_calcwater(sc, n);
+
+ DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode[n]));
+
+ mutex_enter(sc->sc_intr_lock);
+ sc->sc_nmixer_states++;
+ mc = &sc->sc_mixer_state[sc->sc_nmixer_states];
+ mc->dev = sc->sc_nmixer_states;
+ mc->type = AUDIO_MIXER_VALUE;
+ mc->un.value.num_channels = 1;
+ (void)audio_get_port(sc, mc);
- DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode));
+ mutex_exit(sc->sc_intr_lock);
return 0;
bad:
- mutex_enter(sc->sc_intr_lock);
- if (hw->close != NULL)
+ for (i = 0; i < vc->sc_npfilters; i++) {
+ vc->sc_pfilters[i]->dtor(vc->sc_pfilters[i]);
+ vc->sc_pfilters[i] = NULL;
+ audio_stream_dtor(&vc->sc_pstreams[i]);
+ }
+ vc->sc_npfilters = 0;
+ if (hw->close != NULL && sc->sc_opens == 0)
hw->close(sc->hw_hdl);
- sc->sc_open = 0;
- sc->sc_mode = 0;
- mutex_exit(sc->sc_intr_lock);
- sc->sc_full_duplex = 0;
+ sc->sc_opens--;
+ sc->sc_audiopid[n] = -1;
+ mutex_exit(sc->sc_lock);
+ audio_free_ring(sc, &vc->sc_mpr);
+ mutex_enter(sc->sc_lock);
+ kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel));
return error;
}
@@ -1752,14 +1905,19 @@
* Must be called from task context.
*/
void
-audio_init_record(struct audio_softc *sc)
+audio_init_record(struct audio_softc *sc, int n)
{
KASSERT(mutex_owned(sc->sc_lock));
+ struct virtual_channel *vc = sc->sc_vchan[n];
+
+ if (sc->sc_opens != 0)
+ return;
+
mutex_enter(sc->sc_intr_lock);
if (sc->hw_if->speaker_ctl &&
- (!sc->sc_full_duplex || (sc->sc_mode & AUMODE_PLAY) == 0))
+ (!vc->sc_full_duplex || (vc->sc_mode & AUMODE_PLAY) == 0))
sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_OFF);
mutex_exit(sc->sc_intr_lock);
}
@@ -1768,20 +1926,23 @@
* Must be called from task context.
*/
void
-audio_init_play(struct audio_softc *sc)
+audio_init_play(struct audio_softc *sc, int n)
{
KASSERT(mutex_owned(sc->sc_lock));
+
+ if (sc->sc_opens != 0)
+ return;
mutex_enter(sc->sc_intr_lock);
- sc->sc_wstamp = sc->sc_pr.stamp;
+ sc->sc_vchan[n]->sc_wstamp = sc->sc_vchan[n]->sc_mpr.stamp;
if (sc->hw_if->speaker_ctl)
sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_ON);
mutex_exit(sc->sc_intr_lock);
}
int
-audio_drain(struct audio_softc *sc)
+audio_drain(struct audio_softc *sc, int n)
{
struct audio_ringbuffer *cb;
int error, drops;
@@ -1789,19 +1950,19 @@
KASSERT(mutex_owned(sc->sc_lock));
KASSERT(mutex_owned(sc->sc_intr_lock));
-
- DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_pbus));
- cb = &sc->sc_pr;
+
+ DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_pbus[n]));
+ cb = &sc->sc_vchan[n]->sc_mpr;
if (cb->mmapped)
return 0;
- used = audio_stream_get_used(&sc->sc_pr.s);
- for (i = 0; i < sc->sc_npfilters; i++)
- used += audio_stream_get_used(&sc->sc_pstreams[i]);
+ used = audio_stream_get_used(&cb->s);
+ for (i = 0; i < sc->sc_vchan[n]->sc_npfilters; i++)
+ used += audio_stream_get_used(&sc->sc_vchan[n]->sc_pstreams[i]);
if (used <= 0)
return 0;
- if (!sc->sc_pbus) {
+ if (n != 0 && !sc->sc_vchan[n]->sc_pbus) {
/* We've never started playing, probably because the
* block was too short. Pad it and start now.
*/
@@ -1811,7 +1972,7 @@
cc = cb->blksize - (inp - cb->s.start) % cb->blksize;
audio_fill_silence(&cb->s.param, inp, cc);
cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc);
- error = audiostartp(sc);
+ error = audiostartp(sc, n);
if (error)
return error;
}
@@ -1823,15 +1984,15 @@
*/
#ifdef DIAGNOSTIC
if (cb->copying) {
- printf("audio_drain: copying in progress!?!\n");
+ DPRINTF(("audio_drain: copying in progress!?!\n"));
cb->copying = false;
}
#endif
drops = cb->drops;
error = 0;
while (cb->drops == drops && !error) {
- DPRINTF(("audio_drain: used=%d, drops=%ld\n",
- audio_stream_get_used(&sc->sc_pr.s), cb->drops));
+ DPRINTF(("audio_drain: n=%d, used=%d, drops=%ld\n", n,
+ audio_stream_get_used(&sc->sc_vchan[n]->sc_mpr.s), cb->drops));
mutex_exit(sc->sc_intr_lock);
error = audio_waitio(sc, &sc->sc_wchan);
mutex_enter(sc->sc_intr_lock);
@@ -1847,47 +2008,77 @@
/* ARGSUSED */
int
audio_close(struct audio_softc *sc, int flags, int ifmt,
- struct lwp *l)
+ struct lwp *l, int n)
{
+ struct virtual_channel *vc;
const struct audio_hw_if *hw;
+ int o;
KASSERT(mutex_owned(sc->sc_lock));
+
+ if (sc->sc_opens == 0 || n == 0)
+ return ENXIO;
+
+ vc = sc->sc_vchan[n];
- DPRINTF(("audio_close: sc=%p\n", sc));
hw = sc->hw_if;
mutex_enter(sc->sc_intr_lock);
+ DPRINTF(("audio_close: sc=%p\n", sc));
/* Stop recording. */
- if ((flags & FREAD) && sc->sc_rbus) {
+ if (sc->sc_opens == 1 && (flags & FREAD) && sc->sc_rbus) {
/*
* XXX Some drivers (e.g. SB) use the same routine
* to halt input and output so don't halt input if
* in full duplex mode. These drivers should be fixed.
*/
- if (!sc->sc_full_duplex || hw->halt_input != hw->halt_output)
+ if (!vc->sc_full_duplex || hw->halt_input != hw->halt_output)
hw->halt_input(sc->hw_hdl);
sc->sc_rbus = false;
}
/*
* Block until output drains, but allow ^C interrupt.
*/
- sc->sc_pr.usedlow = sc->sc_pr.blksize; /* avoid excessive wakeups */
+ vc->sc_mpr.usedlow = vc->sc_mpr.blksize; /* avoid excessive wakeups */
/*
* If there is pending output, let it drain (unless
* the output is paused).
*/
- if ((flags & FWRITE) && sc->sc_pbus) {
- if (!sc->sc_pr.pause && !audio_drain(sc) && hw->drain)
+ if ((flags & FWRITE) && vc->sc_pbus) {
+ if (!vc->sc_mpr.pause)
+ audio_drain(sc, n);
+ vc->sc_pbus = false;
+ }
+ if (sc->sc_opens == 1) {
+ audio_drain(sc, 0);
+ if (hw->drain)
(void)hw->drain(sc->hw_hdl);
hw->halt_output(sc->hw_hdl);
- sc->sc_pbus = false;
+ sc->sc_trigger_started = false;
}
- if (hw->close != NULL)
+ if (sc->sc_opens == 1 && hw->close != NULL)
hw->close(sc->hw_hdl);
- sc->sc_open = 0;
- sc->sc_mode = 0;
- sc->sc_full_duplex = 0;
+ if (sc->sc_opens == 1)
+ sc->sc_async_audio = 0;
+
+ vc->sc_open = 0;
+ vc->sc_mode = 0;
+ vc->sc_full_duplex = 0;
+
+ for (o = 0; o < vc->sc_npfilters; o++) {
+ vc->sc_pfilters[o]->dtor(vc->sc_pfilters[o]);
+ vc->sc_pfilters[o] = NULL;
+ audio_stream_dtor(&vc->sc_pstreams[o]);
+ }
+ vc->sc_npfilters = 0;
+
+ sc->sc_opens--;
+ sc->sc_nmixer_states--;
+ sc->sc_audiopid[n] = -1;
mutex_exit(sc->sc_intr_lock);
- sc->sc_async_audio = 0;
+ mutex_exit(sc->sc_lock);
+ audio_free_ring(sc, &vc->sc_mpr);
+ mutex_enter(sc->sc_lock);
+ kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel));
return 0;
}
@@ -1896,18 +2087,27 @@
audio_read(struct audio_softc *sc, struct uio *uio, int ioflag)
{
struct audio_ringbuffer *cb;
+ struct virtual_channel *vc;
const uint8_t *outp;
uint8_t *inp;
- int error, used, cc, n;
+ int error, used, cc, n, m;
KASSERT(mutex_owned(sc->sc_lock));
+ for (m = 1; m < VAUDIOCHANS; m++) {
+ if (sc->sc_audiopid[m] == curproc->p_pid)
+ break;
+ }
+ if (m == VAUDIOCHANS)
+ return ENXIO;
+ vc = sc->sc_vchan[m];
+
cb = &sc->sc_rr;
if (cb->mmapped)
return EINVAL;
DPRINTFN(1,("audio_read: cc=%zu mode=%d\n",
- uio->uio_resid, sc->sc_mode));
+ uio->uio_resid, vc->sc_mode));
#ifdef AUDIO_PM_IDLE
if (device_is_active(&sc->dev) || sc->sc_idle)
@@ -1919,18 +2119,18 @@
* If hardware is half-duplex and currently playing, return
* silence blocks based on the number of blocks we have output.
*/
- if (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) {
+ if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) {
while (uio->uio_resid > 0 && !error) {
for(;;) {
/*
* No need to lock, as any wakeup will be
* held for us while holding sc_lock.
*/
- cc = sc->sc_pr.stamp - sc->sc_wstamp;
+ cc = vc->sc_mpr.stamp - vc->sc_wstamp;
if (cc > 0)
break;
DPRINTF(("audio_read: stamp=%lu, wstamp=%lu\n",
- sc->sc_pr.stamp, sc->sc_wstamp));
+ vc->sc_mpr.stamp, vc->sc_wstamp));
if (ioflag & IO_NDELAY)
return EWOULDBLOCK;
error = audio_waitio(sc, &sc->sc_rchan);
@@ -1945,7 +2145,7 @@
DPRINTFN(1,("audio_read: reading in write mode, "
"cc=%d\n", cc));
error = audio_silence_copyout(sc, cc, uio);
- sc->sc_wstamp += cc;
+ vc->sc_wstamp += cc;
}
return error;
}
@@ -1998,45 +2198,47 @@
}
void
-audio_clear(struct audio_softc *sc)
+audio_clear(struct audio_softc *sc, int n)
{
+ struct virtual_channel *vc = sc->sc_vchan[n];
+
KASSERT(mutex_owned(sc->sc_intr_lock));
if (sc->sc_rbus) {
cv_broadcast(&sc->sc_rchan);
- sc->hw_if->halt_input(sc->hw_hdl);
+ if (sc->sc_opens == 1)
+ sc->hw_if->halt_input(sc->hw_hdl);
sc->sc_rbus = false;
sc->sc_rr.pause = false;
}
- if (sc->sc_pbus) {
+ if (vc->sc_pbus) {
cv_broadcast(&sc->sc_wchan);
- sc->hw_if->halt_output(sc->hw_hdl);
- sc->sc_pbus = false;
- sc->sc_pr.pause = false;
+ vc->sc_pbus = false;
+ vc->sc_mpr.pause = false;
}
}
void
-audio_clear_intr_unlocked(struct audio_softc *sc)
+audio_clear_intr_unlocked(struct audio_softc *sc, int n)
{
mutex_enter(sc->sc_intr_lock);
- audio_clear(sc);
+ audio_clear(sc, n);
mutex_exit(sc->sc_intr_lock);
}
void
-audio_calc_blksize(struct audio_softc *sc, int mode)
+audio_calc_blksize(struct audio_softc *sc, int mode, int n)
{
const audio_params_t *parm;
struct audio_ringbuffer *rb;
- if (sc->sc_blkset)
+ if (sc->sc_vchan[n]->sc_blkset)
return;
if (mode == AUMODE_PLAY) {
- rb = &sc->sc_pr;
+ rb = &sc->sc_vchan[n]->sc_mpr ;
parm = &rb->s.param;
} else {
rb = &sc->sc_rr;
@@ -2210,18 +2412,28 @@
uio_fetcher_t ufetcher;
audio_stream_t stream;
struct audio_ringbuffer *cb;
+ struct virtual_channel *vc;
stream_fetcher_t *fetcher;
stream_filter_t *filter;
uint8_t *inp, *einp;
- int saveerror, error, n, cc, used;
+ int saveerror, error, n, m, cc, used;
KASSERT(mutex_owned(sc->sc_lock));
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return EINVAL;
+
+ vc = sc->sc_vchan[n];
+ cb = &vc->sc_mpr;
+
DPRINTFN(2,("audio_write: sc=%p count=%zu used=%d(hi=%d)\n",
- sc, uio->uio_resid, audio_stream_get_used(sc->sc_pustream),
- sc->sc_pr.usedhigh));
- cb = &sc->sc_pr;
- if (cb->mmapped)
+ sc, uio->uio_resid, audio_stream_get_used(vc->sc_pustream),
+ vc->sc_mpr.usedhigh));
+ if (vc->sc_mpr.mmapped)
return EINVAL;
if (uio->uio_resid == 0) {
@@ -2237,20 +2449,20 @@
/*
* If half-duplex and currently recording, throw away data.
*/
- if (!sc->sc_full_duplex &&
- (sc->sc_mode & AUMODE_RECORD)) {
+ if (!vc->sc_full_duplex &&
+ (vc->sc_mode & AUMODE_RECORD)) {
uio->uio_offset += uio->uio_resid;
uio->uio_resid = 0;
DPRINTF(("audio_write: half-dpx read busy\n"));
return 0;
}
- if (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) {
- n = min(sc->sc_playdrop, uio->uio_resid);
- DPRINTF(("audio_write: playdrop %d\n", n));
- uio->uio_offset += n;
- uio->uio_resid -= n;
- sc->sc_playdrop -= n;
+ if (!(vc->sc_mode & AUMODE_PLAY_ALL) && vc->sc_playdrop > 0) {
+ m = min(vc->sc_playdrop, uio->uio_resid);
+ DPRINTF(("audio_write: playdrop %d\n", m));
+ uio->uio_offset += m;
+ uio->uio_resid -= m;
+ vc->sc_playdrop -= m;
if (uio->uio_resid == 0)
return 0;
}
@@ -2258,9 +2470,9 @@
/**
* setup filter pipeline
*/
- uio_fetcher_ctor(&ufetcher, uio, cb->usedhigh);
- if (sc->sc_npfilters > 0) {
- fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base;
+ uio_fetcher_ctor(&ufetcher, uio, vc->sc_mpr.usedhigh);
+ if (vc->sc_npfilters > 0) {
+ fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
} else {
fetcher = &ufetcher.base;
}
@@ -2269,7 +2481,7 @@
mutex_enter(sc->sc_intr_lock);
while (uio->uio_resid > 0 && !error) {
/* wait if the first buffer is occupied */
- while ((used = audio_stream_get_used(sc->sc_pustream))
+ while ((used = audio_stream_get_used(vc->sc_pustream))
>= cb->usedhigh) {
DPRINTFN(2, ("audio_write: sleep used=%d lowat=%d "
"hiwat=%d\n", used,
@@ -2291,17 +2503,17 @@
/* Write to the sc_pustream as much as possible. */
mutex_exit(sc->sc_intr_lock);
- if (sc->sc_npfilters > 0) {
- filter = sc->sc_pfilters[0];
+ if (vc->sc_npfilters > 0) {
+ filter = vc->sc_pfilters[0];
filter->set_fetcher(filter, &ufetcher.base);
- fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base;
+ fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
cc = cb->blksize * 2;
error = fetcher->fetch_to(sc, fetcher, &stream, cc);
if (error != 0) {
fetcher = &ufetcher.base;
- cc = sc->sc_pustream->end - sc->sc_pustream->start;
+ cc = vc->sc_pustream->end - vc->sc_pustream->start;
error = fetcher->fetch_to(sc, fetcher,
- sc->sc_pustream, cc);
+ vc->sc_pustream, cc);
}
} else {
fetcher = &ufetcher.base;
@@ -2309,9 +2521,9 @@
error = fetcher->fetch_to(sc, fetcher, &stream, cc);
}
mutex_enter(sc->sc_intr_lock);
- if (sc->sc_npfilters > 0) {
+ if (vc->sc_npfilters > 0) {
cb->fstamp += ufetcher.last_used
- - audio_stream_get_used(sc->sc_pustream);
+ - audio_stream_get_used(vc->sc_pustream);
}
cb->s.used += stream.used - used;
cb->s.inp = stream.inp;
@@ -2321,7 +2533,7 @@
* This is a very suboptimal way of keeping track of
* silence in the buffer, but it is simple.
*/
- sc->sc_sil_count = 0;
+ vc->sc_sil_count = 0;
/*
* If the interrupt routine wants the last block filled AND
@@ -2338,9 +2550,9 @@
cc = 0;
cb->needfill = false;
cb->copying = false;
- if (!sc->sc_pbus && !cb->pause) {
+ if (!vc->sc_pbus && !cb->pause) {
saveerror = error;
- error = audiostartp(sc);
+ error = audiostartp(sc, n);
if (saveerror != 0) {
/* Report the first error that occurred. */
error = saveerror;
@@ -2361,12 +2573,21 @@
struct lwp *l)
{
const struct audio_hw_if *hw;
+ struct virtual_channel *vc;
struct audio_offset *ao;
u_long stamp;
- int error, offs, fd;
+ int error, offs, fd, n;
bool rbus, pbus;
KASSERT(mutex_owned(sc->sc_lock));
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n >= VAUDIOCHANS)
+ n = 1;
+
+ vc = sc->sc_vchan[n];
DPRINTF(("audio_ioctl(%lu,'%c',%lu)\n",
IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd&0xff));
@@ -2396,18 +2617,18 @@
case AUDIO_FLUSH:
DPRINTF(("AUDIO_FLUSH\n"));
rbus = sc->sc_rbus;
- pbus = sc->sc_pbus;
+ pbus = vc->sc_pbus;
mutex_enter(sc->sc_intr_lock);
- audio_clear(sc);
- error = audio_initbufs(sc);
+ audio_clear(sc, n);
+ error = audio_initbufs(sc, n);
if (error) {
mutex_exit(sc->sc_intr_lock);
return error;
}
- if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_pbus && pbus)
- error = audiostartp(sc);
+ if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_pbus && pbus)
+ error = audiostartp(sc, n);
if (!error &&
- (sc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus)
+ (vc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus)
error = audiostartr(sc);
mutex_exit(sc->sc_intr_lock);
break;
@@ -2421,7 +2642,7 @@
break;
case AUDIO_PERROR:
- *(int *)addr = sc->sc_pr.drops;
+ *(int *)addr = vc->sc_mpr.drops;
break;
/*
@@ -2447,17 +2668,17 @@
ao = (struct audio_offset *)addr;
mutex_enter(sc->sc_intr_lock);
/* figure out where next DMA will start */
- stamp = sc->sc_pustream == &sc->sc_pr.s
- ? sc->sc_pr.stamp : sc->sc_pr.fstamp;
- offs = sc->sc_pustream->outp - sc->sc_pustream->start
- + sc->sc_pr.blksize;
+ stamp = vc->sc_pustream == &vc->sc_mpr.s
+ ? vc->sc_mpr.stamp : vc->sc_mpr.fstamp;
+ offs = vc->sc_pustream->outp - vc->sc_pustream->start
+ + vc->sc_mpr.blksize;
mutex_exit(sc->sc_intr_lock);
ao->samples = stamp;
ao->deltablks =
- (stamp / sc->sc_pr.blksize) -
- (sc->sc_pr.stamp_last / sc->sc_pr.blksize);
- sc->sc_pr.stamp_last = stamp;
- if (sc->sc_pustream->start + offs >= sc->sc_pustream->end)
+ (stamp / vc->sc_mpr.blksize) -
+ (vc->sc_mpr.stamp_last / vc->sc_mpr.blksize);
+ vc->sc_mpr.stamp_last = stamp;
+ if (vc->sc_pustream->start + offs >= vc->sc_pustream->end)
offs = 0;
ao->offset = offs;
break;
@@ -2467,29 +2688,29 @@
* sample of what we write next?
*/
case AUDIO_WSEEK:
- *(u_long *)addr = audio_stream_get_used(sc->sc_pustream);
+ *(u_long *)addr = audio_stream_get_used(vc->sc_pustream);
break;
case AUDIO_SETINFO:
- DPRINTF(("AUDIO_SETINFO mode=0x%x\n", sc->sc_mode));
- error = audiosetinfo(sc, (struct audio_info *)addr);
+ DPRINTF(("AUDIO_SETINFO mode=0x%x\n", vc->sc_mode));
+ error = audiosetinfo(sc, (struct audio_info *)addr, false, n);
break;
case AUDIO_GETINFO:
DPRINTF(("AUDIO_GETINFO\n"));
- error = audiogetinfo(sc, (struct audio_info *)addr, 0);
+ error = audiogetinfo(sc, (struct audio_info *)addr, 0, n);
break;
case AUDIO_GETBUFINFO:
DPRINTF(("AUDIO_GETBUFINFO\n"));
- error = audiogetinfo(sc, (struct audio_info *)addr, 1);
+ error = audiogetinfo(sc, (struct audio_info *)addr, 1, n);
break;
case AUDIO_DRAIN:
DPRINTF(("AUDIO_DRAIN\n"));
mutex_enter(sc->sc_intr_lock);
- error = audio_drain(sc);
- if (!error && hw->drain)
+ error = audio_drain(sc, n);
+ if (!error && sc->sc_opens == 1 && hw->drain)
error = hw->drain(sc->hw_hdl);
mutex_exit(sc->sc_intr_lock);
break;
@@ -2501,13 +2722,13 @@
case AUDIO_GETENC:
DPRINTF(("AUDIO_GETENC\n"));
- error = hw->query_encoding(sc->hw_hdl,
+ error = audio_query_encoding(sc,
(struct audio_encoding *)addr);
break;
case AUDIO_GETFD:
DPRINTF(("AUDIO_GETFD\n"));
- *(int *)addr = sc->sc_full_duplex;
+ *(int *)addr = vc->sc_full_duplex;
break;
case AUDIO_SETFD:
@@ -2519,7 +2740,7 @@
else
error = 0;
if (!error)
- sc->sc_full_duplex = fd;
+ vc->sc_full_duplex = fd;
} else {
if (fd)
error = ENOTTY;
@@ -2550,12 +2771,20 @@
int
audio_poll(struct audio_softc *sc, int events, struct lwp *l)
{
+ struct virtual_channel *vc;
int revents;
- int used;
+ int used, n;
KASSERT(mutex_owned(sc->sc_lock));
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return ENXIO;
+ vc = sc->sc_vchan[n];
- DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, sc->sc_mode));
+ DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, vc->sc_mode));
revents = 0;
mutex_enter(sc->sc_intr_lock);
@@ -2566,23 +2795,23 @@
* silence at the play rate; poll for silence being
* available. Otherwise, poll for recorded sound.
*/
- if ((!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) ?
- sc->sc_pr.stamp > sc->sc_wstamp :
+ if ((!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) ?
+ vc->sc_mpr.stamp > vc->sc_wstamp :
used > sc->sc_rr.usedlow)
revents |= events & (POLLIN | POLLRDNORM);
}
if (events & (POLLOUT | POLLWRNORM)) {
- used = audio_stream_get_used(sc->sc_pustream);
+ used = audio_stream_get_used(vc->sc_pustream);
/*
* If half duplex and recording, audio_write() will throw
* away play data, which means we are always ready to write.
* Otherwise, poll for play buffer being below its low water
* mark.
*/
- if ((!sc->sc_full_duplex && (sc->sc_mode & AUMODE_RECORD)) ||
- (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) ||
- (used <= sc->sc_pr.usedlow))
+ if ((!vc->sc_full_duplex && (vc->sc_mode & AUMODE_RECORD)) ||
+ (!(vc->sc_mode & AUMODE_PLAY_ALL) && vc->sc_playdrop > 0) ||
+ (used <= vc->sc_mpr.usedlow))
revents |= events & (POLLOUT | POLLWRNORM);
}
mutex_exit(sc->sc_intr_lock);
@@ -2613,11 +2842,21 @@
filt_audioread(struct knote *kn, long hint)
{
struct audio_softc *sc;
+ struct virtual_channel *vc;
+ int n;
sc = kn->kn_hook;
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return ENXIO;
+
+ vc = sc->sc_vchan[n];
mutex_enter(sc->sc_intr_lock);
- if (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY))
- kn->kn_data = sc->sc_pr.stamp - sc->sc_wstamp;
+ if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY))
+ kn->kn_data = vc->sc_mpr.stamp - vc->sc_wstamp;
else
kn->kn_data = audio_stream_get_used(sc->sc_rustream)
- sc->sc_rr.usedlow;
@@ -2645,10 +2884,18 @@
{
struct audio_softc *sc;
audio_stream_t *stream;
+ int n;
sc = kn->kn_hook;
mutex_enter(sc->sc_intr_lock);
- stream = sc->sc_pustream;
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return ENXIO;
+
+ stream = sc->sc_vchan[n]->sc_pustream;
kn->kn_data = (stream->end - stream->start)
- audio_stream_get_used(stream);
mutex_exit(sc->sc_intr_lock);
@@ -2694,9 +2941,16 @@
const struct audio_hw_if *hw;
struct audio_ringbuffer *cb;
paddr_t rv;
+ int n;
KASSERT(mutex_owned(sc->sc_lock));
- KASSERT(sc->sc_dvlock > 0);
+
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return -1;
DPRINTF(("audio_mmap: off=%lld, prot=%d\n", (long long)off, prot));
hw = sc->hw_if;
@@ -2717,26 +2971,26 @@
*/
if (prot == (VM_PROT_READ|VM_PROT_WRITE) ||
prot == VM_PROT_WRITE)
- cb = &sc->sc_pr;
+ cb = &sc->sc_vchan[n]->sc_mpr;
else if (prot == VM_PROT_READ)
cb = &sc->sc_rr;
else
return -1;
#else
- cb = &sc->sc_pr;
+ cb = &sc->sc_vchan[n]->sc_mpr;
#endif
if ((u_int)off >= cb->s.bufsize)
return -1;
if (!cb->mmapped) {
cb->mmapped = true;
- if (cb == &sc->sc_pr) {
+ if (cb != &sc->sc_rr) {
audio_fill_silence(&cb->s.param, cb->s.start,
cb->s.bufsize);
mutex_enter(sc->sc_intr_lock);
- sc->sc_pustream = &cb->s;
- if (!sc->sc_pbus && !sc->sc_pr.pause)
- (void)audiostartp(sc);
+ sc->sc_vchan[n]->sc_pustream = &cb->s;
+ if (!sc->sc_vchan[n]->sc_pbus && !sc->sc_vchan[n]->sc_mpr.pause)
+ (void)audiostartp(sc, n);
mutex_exit(sc->sc_intr_lock);
} else {
mutex_enter(sc->sc_intr_lock);
@@ -2785,45 +3039,38 @@
}
int
-audiostartp(struct audio_softc *sc)
+audiostartp(struct audio_softc *sc, int n)
{
- int error;
- int used;
+ struct virtual_channel *vc = sc->sc_vchan[n];
+ int used, error;
KASSERT(mutex_owned(sc->sc_lock));
KASSERT(mutex_owned(sc->sc_intr_lock));
- used = audio_stream_get_used(&sc->sc_pr.s);
+ error = 0;
+ used = audio_stream_get_used(&vc->sc_mpr.s);
DPRINTF(("audiostartp: start=%p used=%d(hi=%d blk=%d) mmapped=%d\n",
- sc->sc_pr.s.start, used, sc->sc_pr.usedhigh,
- sc->sc_pr.blksize, sc->sc_pr.mmapped));
+ vc->sc_mpr.s.start, used, vc->sc_mpr.usedhigh,
+ vc->sc_mpr.blksize, vc->sc_mpr.mmapped));
if (!audio_can_playback(sc))
return EINVAL;
- if (!sc->sc_pr.mmapped && used < sc->sc_pr.blksize) {
+ if (!vc->sc_mpr.mmapped && used < vc->sc_mpr.blksize) {
cv_broadcast(&sc->sc_wchan);
DPRINTF(("%s: wakeup and return\n", __func__));
return 0;
}
-
- if (sc->hw_if->trigger_output) {
- DPRINTF(("%s: call trigger_output\n", __func__));
- error = sc->hw_if->trigger_output(sc->hw_hdl, sc->sc_pr.s.start,
- sc->sc_pr.s.end, sc->sc_pr.blksize,
- audio_pint, (void *)sc, &sc->sc_pr.s.param);
- } else {
- DPRINTF(("%s: call start_output\n", __func__));
- error = sc->hw_if->start_output(sc->hw_hdl,
- __UNCONST(sc->sc_pr.s.outp), sc->sc_pr.blksize,
- audio_pint, (void *)sc);
- }
- if (error) {
- DPRINTF(("audiostartp failed: %d\n", error));
- return error;
+
+ vc->sc_pbus = true;
+ mix_func(sc, &vc->sc_mpr, n);
+ if (sc->sc_trigger_started == false) {
+ mix_write(sc);
+ cv_broadcast(&sc->sc_condvar);
+
}
- sc->sc_pbus = true;
- return 0;
+
+ return error;
}
/*
@@ -2843,15 +3090,16 @@
*/
static inline void
audio_pint_silence(struct audio_softc *sc, struct audio_ringbuffer *cb,
- uint8_t *inp, int cc)
+ uint8_t *inp, int cc, int n)
{
+ struct virtual_channel *vc = sc->sc_vchan[n];
uint8_t *s, *e, *p, *q;
- KASSERT(mutex_owned(sc->sc_intr_lock));
+ KASSERT(mutex_owned(sc->sc_lock));
- if (sc->sc_sil_count > 0) {
- s = sc->sc_sil_start; /* start of silence */
- e = s + sc->sc_sil_count; /* end of sil., may be beyond end */
+ if (vc->sc_sil_count > 0) {
+ s = vc->sc_sil_start; /* start of silence */
+ e = s + vc->sc_sil_count; /* end of sil., may be beyond end */
p = inp; /* adjusted pointer to area to fill */
if (p < s)
p += cb->s.end - cb->s.start;
@@ -2860,10 +3108,10 @@
if (!(s <= p && p < e &&
s <= q && q <= e)) {
if (s <= p)
- sc->sc_sil_count = max(sc->sc_sil_count, q-s);
+ vc->sc_sil_count = max(vc->sc_sil_count, q-s);
DPRINTFN(5,("audio_pint_silence: fill cc=%d inp=%p, "
"count=%d size=%d\n",
- cc, inp, sc->sc_sil_count,
+ cc, inp, vc->sc_sil_count,
(int)(cb->s.end - cb->s.start)));
audio_fill_silence(&cb->s.param, inp, cc);
} else {
@@ -2872,8 +3120,8 @@
}
} else {
- sc->sc_sil_start = inp;
- sc->sc_sil_count = cc;
+ vc->sc_sil_start = inp;
+ vc->sc_sil_count = cc;
DPRINTFN(5, ("audio_pint_silence: start fill %p %d\n",
inp, cc));
audio_fill_silence(&cb->s.param, inp, cc);
@@ -2929,136 +3177,180 @@
void
audio_pint(void *v)
{
+ struct audio_softc *sc;
+ struct virtual_channel *vc;
+
+ sc = v;
+ vc = sc->sc_vchan[0];
+
+ if (sc->sc_dying == true)
+ return;
+
+ vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s,
+ vc->sc_mpr.s.outp, vc->sc_mpr.blksize);
+ mix_write(sc);
+
+ cv_broadcast(&sc->sc_condvar);
+}
+
+void
+audio_mix(void *v)
+{
stream_fetcher_t null_fetcher;
struct audio_softc *sc;
- const struct audio_hw_if *hw;
+ struct virtual_channel *vc;
struct audio_ringbuffer *cb;
stream_fetcher_t *fetcher;
uint8_t *inp;
int cc, used;
int blksize;
- int error;
-
+ int n, i;
+
sc = v;
- KASSERT(mutex_owned(sc->sc_intr_lock));
-
- if (!sc->sc_open)
- return; /* ignore interrupt if not open */
+ DPRINTF(("PINT\n"));
+ sc->schedule_rih = false;
+ sc->schedule_wih = false;
+ sc->sc_writeme = false;
- hw = sc->hw_if;
- cb = &sc->sc_pr;
- blksize = cb->blksize;
- cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize);
- cb->stamp += blksize;
- if (cb->mmapped) {
- DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n",
- cb->s.outp, blksize, cb->s.inp));
- if (hw->trigger_output == NULL)
- (void)hw->start_output(sc->hw_hdl, __UNCONST(cb->s.outp),
- blksize, audio_pint, (void *)sc);
+ if (sc->sc_dying == true)
return;
- }
+
+ i = sc->sc_opens;
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (!sc->sc_opens || i <= 0)
+ break; /* ignore interrupt if not open */
+
+ if (sc->sc_audiopid[n] == -1)
+ continue;
+ i--;
+ vc = sc->sc_vchan[n];
+ if (!vc->sc_open)
+ continue;
+ if (!vc->sc_pbus)
+ continue;
+
+ sc->sc_writeme = true;
+
+ cb = &vc->sc_mpr;
+ blksize = cb->blksize;
+
+ cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize);
+ cb->stamp += blksize;
+ if (cb->mmapped) {
+ DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n",
+ cb->s.outp, blksize, cb->s.inp));
+ mix_func(sc, cb, n);
+ continue;
+ }
#ifdef AUDIO_INTR_TIME
- {
- struct timeval tv;
- u_long t;
- microtime(&tv);
- t = tv.tv_usec + 1000000 * tv.tv_sec;
- if (sc->sc_pnintr) {
- long lastdelta, totdelta;
- lastdelta = t - sc->sc_plastintr - sc->sc_pblktime;
- if (lastdelta > sc->sc_pblktime / 3) {
- printf("audio: play interrupt(%d) off "
+ {
+ struct timeval tv;
+ u_long t;
+ microtime(&tv);
+ t = tv.tv_usec + 1000000 * tv.tv_sec;
+ if (sc->sc_pnintr) {
+ long lastdelta, totdelta;
+ lastdelta = t - sc->sc_plastintr - sc->sc_pblktime;
+ if (lastdelta > sc->sc_pblktime / 3) {
+ printf("audio: play interrupt(%d) off "
"relative by %ld us (%lu)\n",
- sc->sc_pnintr, lastdelta,
- sc->sc_pblktime);
- }
- totdelta = t - sc->sc_pfirstintr -
- sc->sc_pblktime * sc->sc_pnintr;
- if (totdelta > sc->sc_pblktime) {
- printf("audio: play interrupt(%d) off "
- "absolute by %ld us (%lu) (LOST)\n",
- sc->sc_pnintr, totdelta,
- sc->sc_pblktime);
- sc->sc_pnintr++; /* avoid repeated messages */
- }
- } else
- sc->sc_pfirstintr = t;
- sc->sc_plastintr = t;
- sc->sc_pnintr++;
- }
+ sc->sc_pnintr, lastdelta,
+ sc->sc_pblktime);
+ }
+ totdelta = t - sc->sc_pfirstintr -
+ sc->sc_pblktime * sc->sc_pnintr;
+ if (totdelta > sc->sc_pblktime) {
+ printf("audio: play interrupt(%d) off "
+ "absolute by %ld us (%lu) (LOST)\n",
+ sc->sc_pnintr, totdelta,
+ sc->sc_pblktime);
+ sc->sc_pnintr++; /* avoid repeated messages */
+ }
+ } else
+ sc->sc_pfirstintr = t;
+ sc->sc_plastintr = t;
+ sc->sc_pnintr++;
+ }
#endif
- used = audio_stream_get_used(&cb->s);
- /*
- * "used <= cb->usedlow" should be "used < blksize" ideally.
- * Some HW drivers such as uaudio(4) does not call audio_pint()
- * at accurate timing. If used < blksize, uaudio(4) already
- * request transfer of garbage data.
- */
- if (used <= cb->usedlow && !cb->copying && sc->sc_npfilters > 0) {
- /* we might have data in filter pipeline */
- null_fetcher.fetch_to = null_fetcher_fetch_to;
- fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base;
- sc->sc_pfilters[0]->set_fetcher(sc->sc_pfilters[0],
- &null_fetcher);
- used = audio_stream_get_used(sc->sc_pustream);
- cc = cb->s.end - cb->s.start;
- if (blksize * 2 < cc)
- cc = blksize * 2;
- fetcher->fetch_to(sc, fetcher, &cb->s, cc);
- cb->fstamp += used - audio_stream_get_used(sc->sc_pustream);
used = audio_stream_get_used(&cb->s);
- }
- if (used < blksize) {
- /* we don't have a full block to use */
- if (cb->copying) {
- /* writer is in progress, don't disturb */
- cb->needfill = true;
- DPRINTFN(1, ("audio_pint: copying in progress\n"));
- } else {
- inp = cb->s.inp;
- cc = blksize - (inp - cb->s.start) % blksize;
- if (cb->pause)
- cb->pdrops += cc;
- else {
- cb->drops += cc;
- sc->sc_playdrop += cc;
- }
- audio_pint_silence(sc, cb, inp, cc);
- cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc);
-
- /* Clear next block so we keep ahead of the DMA. */
+ /*
+ * "used <= cb->usedlow" should be "used < blksize" ideally.
+ * Some HW drivers such as uaudio(4) does not call audio_pint()
+ * at accurate timing. If used < blksize, uaudio(4) already
+ * request transfer of garbage data.
+ */
+ if (used <= cb->usedlow && !cb->copying && vc->sc_npfilters > 0) {
+ /* we might have data in filter pipeline */
+ null_fetcher.fetch_to = null_fetcher_fetch_to;
+ fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
+ vc->sc_pfilters[0]->set_fetcher(vc->sc_pfilters[0],
+ &null_fetcher);
+ used = audio_stream_get_used(vc->sc_pustream);
+ cc = cb->s.end - cb->s.start;
+ if (blksize * 2 < cc)
+ cc = blksize * 2;
+ fetcher->fetch_to(sc, fetcher, &cb->s, cc);
+ cb->fstamp += used - audio_stream_get_used(vc->sc_pustream);
used = audio_stream_get_used(&cb->s);
- if (used + blksize < cb->s.end - cb->s.start)
- audio_pint_silence(sc, cb, cb->s.inp, blksize);
}
- }
+ if (used < blksize) {
+ /* we don't have a full block to use */
+ if (cb->copying) {
+ /* writer is in progress, don't disturb */
+ cb->needfill = true;
+ DPRINTFN(1, ("audio_pint: copying in progress\n"));
+ } else {
+ inp = cb->s.inp;
+ cc = blksize - (inp - cb->s.start) % blksize;
+ if (cb->pause)
+ cb->pdrops += cc;
+ else {
+ cb->drops += cc;
+ vc->sc_playdrop += cc;
+ }
- DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp, blksize));
- if (hw->trigger_output == NULL) {
- error = hw->start_output(sc->hw_hdl, __UNCONST(cb->s.outp),
- blksize, audio_pint, (void *)sc);
- if (error) {
- /* XXX does this really help? */
- DPRINTF(("audio_pint restart failed: %d\n", error));
- audio_clear(sc);
+ audio_pint_silence(sc, cb, inp, cc, n);
+ cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc);
+
+ /* Clear next block so we keep ahead of the DMA. */
+ used = audio_stream_get_used(&cb->s);
+ if (used + blksize < cb->s.end - cb->s.start)
+ audio_pint_silence(sc, cb, cb->s.inp, blksize, n);
+ }
}
- }
- DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n",
- sc->sc_mode, cb->pause,
- audio_stream_get_used(sc->sc_pustream), cb->usedlow));
- if ((sc->sc_mode & AUMODE_PLAY) && !cb->pause) {
- if (audio_stream_get_used(sc->sc_pustream) <= cb->usedlow)
- softint_schedule(sc->sc_sih_wr);
+ DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp, blksize));
+ mix_func(sc, cb, n);
+
+ DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n",
+ sc->sc_mode[n], cb->pause,
+ audio_stream_get_used(vc->sc_pustream), cb->usedlow));
+
+ if ((vc->sc_mode & AUMODE_PLAY) && !cb->pause) {
+ if (audio_stream_get_used(&cb->s) <= cb->usedlow)
+ sc->schedule_wih = true;
+ }
+ /* Possible to return one or more "phantom blocks" now. */
+ if (!vc->sc_full_duplex && vc->sc_mode & AUMODE_RECORD)
+ sc->schedule_rih = true;
}
- /* Possible to return one or more "phantom blocks" now. */
- if (!sc->sc_full_duplex)
+ kpreempt_disable();
+ if (sc->schedule_wih == true)
+ softint_schedule(sc->sc_sih_wr);
+
+ if (sc->schedule_rih == true)
softint_schedule(sc->sc_sih_rd);
+ kpreempt_enable();
+
+ cc = sc->sc_vchan[0]->sc_mpr.blksize;
+ if (sc->sc_writeme == false) {
+ sc->sc_vchan[0]->sc_mpr.drops += cc;
+ cv_broadcast(&sc->sc_wchan);
+ }
}
/*
@@ -3083,8 +3375,7 @@
cb = &sc->sc_rr;
KASSERT(mutex_owned(sc->sc_intr_lock));
-
- if (!sc->sc_open)
+ if (!sc->sc_opens)
return; /* ignore interrupt if not open */
hw = sc->hw_if;
@@ -3162,7 +3453,7 @@
if (error) {
/* XXX does this really help? */
DPRINTF(("audio_rint: restart failed: %d\n", error));
- audio_clear(sc);
+ audio_clear(sc, 0);
}
}
@@ -3242,17 +3533,66 @@
return 0;
}
+static int
+audio_set_vchan_defaults(struct audio_softc *sc, u_int mode,
+ const struct audio_format *format, int n)
+{
+ struct virtual_channel *vc = sc->sc_vchan[n];
+ struct audio_info ai;
+ int i, error;
+
+ KASSERT(mutex_owned(sc->sc_lock));
+
+ /* default parameters */
+ sc->sc_rparams = vchan_default;
+ vc->sc_pparams = vchan_default;
+ vc->sc_blkset = false;
+
+ AUDIO_INITINFO(&ai);
+ ai.record.sample_rate = vc->sc_pparams.sample_rate;
+ ai.record.encoding = vc->sc_pparams.encoding;
+ ai.record.channels = vc->sc_pparams.channels;
+ ai.record.precision = vc->sc_pparams.precision;
+ ai.record.pause = false;
+ ai.play.sample_rate = format->frequency[0];
+ ai.play.encoding = format->encoding;
+ ai.play.channels = format->channels;
+ ai.play.precision = format->precision;
+ ai.play.pause = false;
+ ai.mode = mode;
+
+ error = audiosetinfo(sc, &ai, true, n);
+ if (vc->sc_npfilters > 0) {
+ ai.play.sample_rate = vc->sc_pstreams[vc->sc_npfilters -1].param.sample_rate;
+ ai.record.sample_rate =
+ vc->sc_pstreams[vc->sc_npfilters -1].param.sample_rate;
+ vc->sc_pparams.sample_rate = ai.play.sample_rate;
+ sc->sc_rparams.sample_rate = ai.record.sample_rate;
+
+ for (i = 0; i < vc->sc_npfilters; i++) {
+ vc->sc_pfilters[i]->dtor(vc->sc_pfilters[i]);
+ vc->sc_pfilters[i] = NULL;
+ audio_stream_dtor(&vc->sc_pstreams[i]);
+ }
+ vc->sc_npfilters = 0;
+
+ return audiosetinfo(sc, &ai, true, n);
+ } else
+ return error;
+}
+
int
-audio_set_defaults(struct audio_softc *sc, u_int mode)
+audio_set_defaults(struct audio_softc *sc, u_int mode, int n)
{
+ struct virtual_channel *vc = sc->sc_vchan[n];
struct audio_info ai;
KASSERT(mutex_owned(sc->sc_lock));
/* default parameters */
sc->sc_rparams = audio_default;
- sc->sc_pparams = audio_default;
- sc->sc_blkset = false;
+ vc->sc_pparams = audio_default;
+ vc->sc_blkset = false;
AUDIO_INITINFO(&ai);
ai.record.sample_rate = sc->sc_rparams.sample_rate;
@@ -3260,14 +3600,14 @@
ai.record.channels = sc->sc_rparams.channels;
ai.record.precision = sc->sc_rparams.precision;
ai.record.pause = false;
- ai.play.sample_rate = sc->sc_pparams.sample_rate;
- ai.play.encoding = sc->sc_pparams.encoding;
- ai.play.channels = sc->sc_pparams.channels;
- ai.play.precision = sc->sc_pparams.precision;
+ ai.play.sample_rate = vc->sc_pparams.sample_rate;
+ ai.play.encoding = vc->sc_pparams.encoding;
+ ai.play.channels = vc->sc_pparams.channels;
+ ai.play.precision = vc->sc_pparams.precision;
ai.play.pause = false;
ai.mode = mode;
- return audiosetinfo(sc, &ai);
+ return audiosetinfo(sc, &ai, true, n);
}
int
@@ -3280,11 +3620,11 @@
ct->un.value.num_channels = 2;
ct->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
ct->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
- if (sc->hw_if->set_port(sc->hw_hdl, ct) == 0)
+ if (audio_set_port(sc, ct) == 0)
return 0;
ct->un.value.num_channels = 1;
ct->un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
- return sc->hw_if->set_port(sc->hw_hdl, ct);
+ return audio_set_port(sc, ct);
}
int
@@ -3322,7 +3662,7 @@
ct.dev = ports->index;
if (ports->isenum) {
ct.type = AUDIO_MIXER_ENUM;
- error = sc->hw_if->get_port(sc->hw_hdl, &ct);
+ error = audio_get_port(sc, &ct);
if (error)
return error;
if (ports->isdual) {
@@ -3344,7 +3684,7 @@
}
} else {
ct.type = AUDIO_MIXER_SET;
- error = sc->hw_if->get_port(sc->hw_hdl, &ct);
+ error = audio_get_port(sc, &ct);
if (error)
return error;
mask = ct.un.mask;
@@ -3374,12 +3714,12 @@
KASSERT(mutex_owned(sc->sc_lock));
ct->un.value.num_channels = 2;
- if (sc->hw_if->get_port(sc->hw_hdl, ct) == 0) {
+ if (audio_get_port(sc, ct) == 0) {
*l = ct->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
*r = ct->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
} else {
ct->un.value.num_channels = 1;
- error = sc->hw_if->get_port(sc->hw_hdl, ct);
+ error = audio_get_port(sc, ct);
if (error)
return error;
*r = *l = ct->un.value.level[AUDIO_MIXER_LEVEL_MONO];
@@ -3411,7 +3751,7 @@
ct.dev = ports->index;
if (ports->isenum) {
ct.type = AUDIO_MIXER_ENUM;
- if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+ if (audio_get_port(sc, &ct))
goto bad;
ct.type = AUDIO_MIXER_VALUE;
if (ports->isdual) {
@@ -3434,7 +3774,7 @@
}
} else {
ct.type = AUDIO_MIXER_SET;
- if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+ if (audio_get_port(sc, &ct))
goto bad;
ct.type = AUDIO_MIXER_VALUE;
lgain = rgain = n = 0;
@@ -3511,7 +3851,7 @@
} else {
ct.un.ord = ports->misel[i];
}
- error = sc->hw_if->set_port(sc->hw_hdl, &ct);
+ error = audio_set_port(sc, &ct);
break;
}
} else {
@@ -3523,7 +3863,7 @@
if (port != 0 && ct.un.mask == 0)
error = EINVAL;
else
- error = sc->hw_if->set_port(sc->hw_hdl, &ct);
+ error = audio_set_port(sc, &ct);
}
if (!error)
mixer_signal(sc);
@@ -3542,7 +3882,7 @@
return 0;
ct.dev = ports->index;
ct.type = ports->isenum ? AUDIO_MIXER_ENUM : AUDIO_MIXER_SET;
- if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+ if (audio_get_port(sc, &ct))
return 0;
aumask = 0;
if (ports->isenum) {
@@ -3565,12 +3905,13 @@
}
int
-audiosetinfo(struct audio_softc *sc, struct audio_info *ai)
+audiosetinfo(struct audio_softc *sc, struct audio_info *ai, bool reset, int n)
{
stream_filter_list_t pfilters, rfilters;
audio_params_t pp, rp;
struct audio_prinfo *r, *p;
const struct audio_hw_if *hw;
+ struct virtual_channel *vc;
audio_stream_t *oldpus, *oldrus;
int setmode;
int error;
@@ -3584,6 +3925,7 @@
KASSERT(mutex_owned(sc->sc_lock));
+ vc = sc->sc_vchan[n];
hw = sc->hw_if;
if (hw == NULL) /* HW has not attached */
return ENXIO;
@@ -3592,13 +3934,13 @@
r = &ai->record;
p = &ai->play;
rbus = sc->sc_rbus;
- pbus = sc->sc_pbus;
+ pbus = vc->sc_pbus;
error = 0;
cleared = false;
modechange = false;
pausechange = false;
- pp = sc->sc_pparams; /* Temporary encoding storage in */
+ pp = vc->sc_pparams; /* Temporary encoding storage in */
rp = sc->sc_rparams; /* case setting the modes fails. */
nr = np = 0;
@@ -3655,13 +3997,13 @@
if (np > 0 && (error = audio_check_params(&pp)))
return error;
- oldpblksize = sc->sc_pr.blksize;
+ oldpblksize = vc->sc_mpr.blksize;
oldrblksize = sc->sc_rr.blksize;
setmode = 0;
if (nr > 0) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
modechange = true;
@@ -3669,7 +4011,7 @@
}
if (np > 0) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
modechange = true;
@@ -3678,19 +4020,19 @@
if (SPECIFIED(ai->mode)) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
modechange = true;
- sc->sc_mode = ai->mode;
- if (sc->sc_mode & AUMODE_PLAY_ALL)
- sc->sc_mode |= AUMODE_PLAY;
- if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_full_duplex)
+ vc->sc_mode = ai->mode;
+ if (vc->sc_mode & AUMODE_PLAY_ALL)
+ vc->sc_mode |= AUMODE_PLAY;
+ if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_full_duplex)
/* Play takes precedence */
- sc->sc_mode &= ~AUMODE_RECORD;
+ vc->sc_mode &= ~AUMODE_RECORD;
}
- oldpus = sc->sc_pustream;
+ oldpus = vc->sc_pustream;
oldrus = sc->sc_rustream;
if (modechange) {
int indep;
@@ -3712,12 +4054,12 @@
rfilters.set = stream_filter_list_set;
/* Some device drivers change channels/sample_rate and change
* no channels/sample_rate. */
- error = hw->set_params(sc->hw_hdl, setmode,
- sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp,
- &pfilters, &rfilters);
+ error = audio_set_params(sc, setmode,
+ vc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp,
+ &pfilters, &rfilters, n);
if (error) {
- DPRINTF(("%s: hw->set_params() failed with %d\n",
- __func__, error));
+ DPRINTF(("%s: audio_set_params() failed with %d\n",
+ __func__, error));
goto cleanup;
}
@@ -3733,7 +4075,7 @@
}
}
- if (sc->sc_pr.mmapped && pfilters.req_size > 0) {
+ if (vc->sc_mpr.mmapped && pfilters.req_size > 0) {
DPRINTF(("%s: mmapped, and filters are requested.\n",
__func__));
error = EINVAL;
@@ -3742,7 +4084,7 @@
/* construct new filter chain */
if (setmode & AUMODE_PLAY) {
- error = audio_setup_pfilters(sc, &pp, &pfilters);
+ error = audio_setup_pfilters(sc, &pp, &pfilters, n);
if (error)
goto cleanup;
}
@@ -3754,25 +4096,29 @@
DPRINTF(("%s: filter setup is completed.\n", __func__));
/* userland formats */
- sc->sc_pparams = pp;
+ vc->sc_pparams = pp;
sc->sc_rparams = rp;
}
/* Play params can affect the record params, so recalculate blksize. */
if (nr > 0 || np > 0) {
- audio_calc_blksize(sc, AUMODE_RECORD);
- audio_calc_blksize(sc, AUMODE_PLAY);
+ audio_calc_blksize(sc, AUMODE_RECORD, n);
+ audio_calc_blksize(sc, AUMODE_PLAY, n);
}
#ifdef AUDIO_DEBUG
- if (audiodebug > 1 && nr > 0)
- audio_print_params("audiosetinfo() After setting record params:", &sc->sc_rparams);
- if (audiodebug > 1 && np > 0)
- audio_print_params("audiosetinfo() After setting play params:", &sc->sc_pparams);
+ if (audiodebug > 1 && nr > 0) {
+ audio_print_params("audiosetinfo() After setting record params:",
+ &sc->sc_rparams);
+ }
+ if (audiodebug > 1 && np > 0) {
+ audio_print_params("audiosetinfo() After setting play params:",
+ &vc->sc_pparams);
+ }
#endif
if (SPECIFIED(p->port)) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
error = au_set_port(sc, &sc->sc_outports, p->port);
@@ -3781,7 +4127,7 @@
}
if (SPECIFIED(r->port)) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
error = au_set_port(sc, &sc->sc_inports, r->port);
@@ -3821,13 +4167,13 @@
ct.type = AUDIO_MIXER_VALUE;
ct.un.value.num_channels = 1;
ct.un.value.level[AUDIO_MIXER_LEVEL_MONO] = ai->monitor_gain;
- error = sc->hw_if->set_port(sc->hw_hdl, &ct);
+ error = audio_set_port(sc, &ct);
if (error)
goto cleanup;
}
if (SPECIFIED_CH(p->pause)) {
- sc->sc_pr.pause = p->pause;
+ vc->sc_mpr.pause = p->pause;
pbus = !p->pause;
pausechange = true;
}
@@ -3843,34 +4189,34 @@
/* Block size specified explicitly. */
if (ai->blocksize == 0) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
- sc->sc_blkset = false;
- audio_calc_blksize(sc, AUMODE_RECORD);
- audio_calc_blksize(sc, AUMODE_PLAY);
+ vc->sc_blkset = false;
+ audio_calc_blksize(sc, AUMODE_RECORD, n);
+ audio_calc_blksize(sc, AUMODE_PLAY, n);
} else {
- sc->sc_blkset = true;
+ vc->sc_blkset = true;
/* check whether new blocksize changes actually */
if (hw->round_blocksize == NULL) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
- sc->sc_pr.blksize = ai->blocksize;
+ vc->sc_mpr.blksize = ai->blocksize;
sc->sc_rr.blksize = ai->blocksize;
} else {
pblksize = hw->round_blocksize(sc->hw_hdl,
- ai->blocksize, AUMODE_PLAY, &sc->sc_pr.s.param);
+ ai->blocksize, AUMODE_PLAY, &vc->sc_mpr.s.param);
rblksize = hw->round_blocksize(sc->hw_hdl,
ai->blocksize, AUMODE_RECORD, &sc->sc_rr.s.param);
- if (pblksize != sc->sc_pr.blksize ||
+ if (pblksize != vc->sc_mpr.blksize ||
rblksize != sc->sc_rr.blksize) {
if (!cleared) {
- audio_clear_intr_unlocked(sc);
+ audio_clear_intr_unlocked(sc, n);
cleared = true;
}
- sc->sc_pr.blksize = ai->blocksize;
+ vc->sc_mpr.blksize = ai->blocksize;
sc->sc_rr.blksize = ai->blocksize;
}
}
@@ -3878,38 +4224,38 @@
}
if (SPECIFIED(ai->mode)) {
- if (sc->sc_mode & AUMODE_PLAY)
- audio_init_play(sc);
- if (sc->sc_mode & AUMODE_RECORD)
- audio_init_record(sc);
+ if (vc->sc_mode & AUMODE_PLAY)
+ audio_init_play(sc, n);
+ if (vc->sc_mode & AUMODE_RECORD)
+ audio_init_record(sc, n);
}
- if (hw->commit_settings) {
+ if (hw->commit_settings && sc->sc_opens == 0) {
error = hw->commit_settings(sc->hw_hdl);
if (error)
goto cleanup;
}
- sc->sc_lastinfo = *ai;
- sc->sc_lastinfovalid = true;
+ vc->sc_lastinfo = *ai;
+ vc->sc_lastinfovalid = true;
cleanup:
- if (cleared || pausechange) {
+ if (cleared || pausechange|| reset) {
int init_error;
mutex_enter(sc->sc_intr_lock);
- init_error = audio_initbufs(sc);
+ init_error = audio_initbufs(sc, n);
if (init_error) goto err;
- if (sc->sc_pr.blksize != oldpblksize ||
+ if (vc->sc_mpr.blksize != oldpblksize ||
sc->sc_rr.blksize != oldrblksize ||
- sc->sc_pustream != oldpus ||
+ vc->sc_pustream != oldpus ||
sc->sc_rustream != oldrus)
- audio_calcwater(sc);
- if ((sc->sc_mode & AUMODE_PLAY) &&
- pbus && !sc->sc_pbus)
- init_error = audiostartp(sc);
+ audio_calcwater(sc, n);
+ if ((vc->sc_mode & AUMODE_PLAY) &&
+ pbus && !vc->sc_pbus)
+ init_error = audiostartp(sc, n);
if (!init_error &&
- (sc->sc_mode & AUMODE_RECORD) &&
+ (vc->sc_mode & AUMODE_RECORD) &&
rbus && !sc->sc_rbus)
init_error = audiostartr(sc);
err:
@@ -3921,48 +4267,51 @@
/* Change water marks after initializing the buffers. */
if (SPECIFIED(ai->hiwat)) {
blks = ai->hiwat;
- if (blks > sc->sc_pr.maxblks)
- blks = sc->sc_pr.maxblks;
+ if (blks > vc->sc_mpr.maxblks)
+ blks = vc->sc_mpr.maxblks;
if (blks < 2)
blks = 2;
- sc->sc_pr.usedhigh = blks * sc->sc_pr.blksize;
+ vc->sc_mpr.usedhigh = blks * vc->sc_mpr.blksize;
}
if (SPECIFIED(ai->lowat)) {
blks = ai->lowat;
- if (blks > sc->sc_pr.maxblks - 1)
- blks = sc->sc_pr.maxblks - 1;
- sc->sc_pr.usedlow = blks * sc->sc_pr.blksize;
+ if (blks > vc->sc_mpr.maxblks - 1)
+ blks = vc->sc_mpr.maxblks - 1;
+ vc->sc_mpr.usedlow = blks * vc->sc_mpr.blksize;
}
if (SPECIFIED(ai->hiwat) || SPECIFIED(ai->lowat)) {
- if (sc->sc_pr.usedlow > sc->sc_pr.usedhigh - sc->sc_pr.blksize)
- sc->sc_pr.usedlow =
- sc->sc_pr.usedhigh - sc->sc_pr.blksize;
+ if (vc->sc_mpr.usedlow > vc->sc_mpr.usedhigh - vc->sc_mpr.blksize)
+ vc->sc_mpr.usedlow =
+ vc->sc_mpr.usedhigh - vc->sc_mpr.blksize;
}
return error;
}
int
-audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode)
+audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode,
+ int n)
{
struct audio_prinfo *r, *p;
const struct audio_hw_if *hw;
+ struct virtual_channel *vc;
KASSERT(mutex_owned(sc->sc_lock));
r = &ai->record;
p = &ai->play;
+ vc = sc->sc_vchan[n];
hw = sc->hw_if;
if (hw == NULL) /* HW has not attached */
return ENXIO;
- p->sample_rate = sc->sc_pparams.sample_rate;
+ p->sample_rate = vc->sc_pparams.sample_rate;
r->sample_rate = sc->sc_rparams.sample_rate;
- p->channels = sc->sc_pparams.channels;
+ p->channels = vc->sc_pparams.channels;
r->channels = sc->sc_rparams.channels;
- p->precision = sc->sc_pparams.precision;
+ p->precision = vc->sc_pparams.precision;
r->precision = sc->sc_rparams.precision;
- p->encoding = sc->sc_pparams.encoding;
+ p->encoding = vc->sc_pparams.encoding;
r->encoding = sc->sc_rparams.encoding;
if (buf_only_mode) {
@@ -3984,7 +4333,7 @@
r->avail_ports = sc->sc_inports.allports;
p->avail_ports = sc->sc_outports.allports;
- au_get_gain(sc, &sc->sc_inports, &r->gain, &r->balance);
+ au_get_gain(sc, &sc->sc_inports, &r->gain, &r->balance);
au_get_gain(sc, &sc->sc_outports, &p->gain, &p->balance);
}
@@ -3994,7 +4343,7 @@
ct.dev = sc->sc_monitor_port;
ct.type = AUDIO_MIXER_VALUE;
ct.un.value.num_channels = 1;
- if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+ if (audio_get_port(sc, &ct))
ai->monitor_gain = 0;
else
ai->monitor_gain =
@@ -4002,45 +4351,45 @@
} else
ai->monitor_gain = 0;
- p->seek = audio_stream_get_used(sc->sc_pustream);
+ p->seek = audio_stream_get_used(vc->sc_pustream);
r->seek = audio_stream_get_used(sc->sc_rustream);
/*
* XXX samples should be a value for userland data.
* But drops is a value for HW data.
*/
- p->samples = (sc->sc_pustream == &sc->sc_pr.s
- ? sc->sc_pr.stamp : sc->sc_pr.fstamp) - sc->sc_pr.drops;
+ p->samples = (vc->sc_pustream == &vc->sc_mpr.s
+ ? vc->sc_mpr.stamp : vc->sc_mpr.fstamp) - vc->sc_mpr.drops;
r->samples = (sc->sc_rustream == &sc->sc_rr.s
? sc->sc_rr.stamp : sc->sc_rr.fstamp) - sc->sc_rr.drops;
p->eof = sc->sc_eof;
r->eof = 0;
- p->pause = sc->sc_pr.pause;
+ p->pause = vc->sc_mpr.pause;
r->pause = sc->sc_rr.pause;
- p->error = sc->sc_pr.drops != 0;
+ p->error = vc->sc_mpr.drops != 0;
r->error = sc->sc_rr.drops != 0;
p->waiting = r->waiting = 0; /* open never hangs */
- p->open = (sc->sc_open & AUOPEN_WRITE) != 0;
- r->open = (sc->sc_open & AUOPEN_READ) != 0;
+ p->open = (vc->sc_open & AUOPEN_WRITE) != 0;
+ r->open = (vc->sc_open & AUOPEN_READ) != 0;
- p->active = sc->sc_pbus;
+ p->active = vc->sc_pbus;
r->active = sc->sc_rbus;
- p->buffer_size = sc->sc_pustream ? sc->sc_pustream->bufsize : 0;
+ p->buffer_size = vc->sc_pustream ? vc->sc_pustream->bufsize : 0;
r->buffer_size = sc->sc_rustream ? sc->sc_rustream->bufsize : 0;
- ai->blocksize = sc->sc_pr.blksize;
- if (sc->sc_pr.blksize > 0) {
- ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize;
- ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize;
+ ai->blocksize = vc->sc_mpr.blksize;
+ if (vc->sc_mpr.blksize > 0) {
+ ai->hiwat = vc->sc_mpr.usedhigh / vc->sc_mpr.blksize;
+ ai->lowat = vc->sc_mpr.usedlow / vc->sc_mpr.blksize;
} else
ai->hiwat = ai->lowat = 0;
- ai->mode = sc->sc_mode;
+ ai->mode = vc->sc_mode;
return 0;
}
@@ -4162,7 +4511,7 @@
case AUDIO_MIXER_DEVINFO:
DPRINTF(("AUDIO_MIXER_DEVINFO\n"));
((mixer_devinfo_t *)addr)->un.v.delta = 0; /* default */
- error = hw->query_devinfo(sc->hw_hdl, (mixer_devinfo_t *)addr);
+ error = audio_query_devinfo(sc, (mixer_devinfo_t *)addr);
break;
case AUDIO_MIXER_READ:
@@ -4170,7 +4519,7 @@
mc = (mixer_ctrl_t *)addr;
if (device_is_active(sc->sc_dev))
- error = hw->get_port(sc->hw_hdl, mc);
+ error = audio_get_port(sc, mc);
else if (mc->dev >= sc->sc_nmixer_states)
error = ENXIO;
else {
@@ -4183,7 +4532,7 @@
case AUDIO_MIXER_WRITE:
DPRINTF(("AUDIO_MIXER_WRITE\n"));
- error = hw->set_port(sc->hw_hdl, (mixer_ctrl_t *)addr);
+ error = audio_set_port(sc, (mixer_ctrl_t *)addr);
if (!error && hw->commit_settings)
error = hw->commit_settings(sc->hw_hdl);
if (!error)
@@ -4263,7 +4612,7 @@
KASSERT(mutex_owned(sc->sc_lock));
for (mi.index = 0;; mi.index++) {
- if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) != 0)
+ if (audio_query_devinfo(sc, &mi) != 0)
break;
KASSERT(mi.index < sc->sc_nmixer_states);
if (mi.type == AUDIO_MIXER_CLASS)
@@ -4272,7 +4621,7 @@
mc->dev = mi.index;
mc->type = mi.type;
mc->un.value.num_channels = mi.un.v.num_channels;
- (void)sc->hw_if->get_port(sc->hw_hdl, mc);
+ (void)audio_get_port(sc, mc);
}
return;
@@ -4287,12 +4636,12 @@
KASSERT(mutex_owned(sc->sc_lock));
for (mi.index = 0; ; mi.index++) {
- if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) != 0)
+ if (audio_query_devinfo(sc, &mi) != 0)
break;
if (mi.type == AUDIO_MIXER_CLASS)
continue;
mc = &sc->sc_mixer_state[mi.index];
- (void)sc->hw_if->set_port(sc->hw_hdl, mc);
+ (void)audio_set_port(sc, mc);
}
if (sc->hw_if->commit_settings)
sc->hw_if->commit_settings(sc->hw_hdl);
@@ -4347,11 +4696,19 @@
{
struct audio_softc *sc = device_private(dv);
const struct audio_hw_if *hwp = sc->hw_if;
+ int n;
mutex_enter(sc->sc_lock);
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+ if (n == VAUDIOCHANS)
+ return false;
+
audio_mixer_capture(sc);
mutex_enter(sc->sc_intr_lock);
- if (sc->sc_pbus == true)
+ if (sc->sc_vchan[n]->sc_pbus == true)
hwp->halt_output(sc->hw_hdl);
if (sc->sc_rbus == true)
hwp->halt_input(sc->hw_hdl);
@@ -4368,14 +4725,25 @@
audio_resume(device_t dv, const pmf_qual_t *qual)
{
struct audio_softc *sc = device_private(dv);
+ struct virtual_channel *vc;
+ int n;
+
+ for (n = 1; n < VAUDIOCHANS; n++) {
+ if (sc->sc_audiopid[n] == curproc->p_pid)
+ break;
+ }
+
+ if (n == VAUDIOCHANS)
+ return false;
+ vc = sc->sc_vchan[n];
mutex_enter(sc->sc_lock);
- if (sc->sc_lastinfovalid)
- audiosetinfo(sc, &sc->sc_lastinfo);
+ if (vc->sc_lastinfovalid)
+ audiosetinfo(sc, &vc->sc_lastinfo, true, n);
audio_mixer_restore(sc);
mutex_enter(sc->sc_intr_lock);
- if ((sc->sc_pbus == true) && !sc->sc_pr.pause)
- audiostartp(sc);
+ if ((vc->sc_pbus == true) && !vc->sc_mpr.pause)
+ audiostartp(sc, n);
if ((sc->sc_rbus == true) && !sc->sc_rr.pause)
audiostartr(sc);
mutex_exit(sc->sc_intr_lock);
@@ -4397,7 +4765,7 @@
if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) {
mi.index = sc->sc_outports.master;
mi.un.v.delta = 0;
- if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0) {
+ if (audio_query_devinfo(sc, &mi) == 0) {
au_get_gain(sc, &sc->sc_outports, &gain, &balance);
newgain = gain - mi.un.v.delta;
if (newgain < AUDIO_MIN_GAIN)
@@ -4420,7 +4788,7 @@
if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) {
mi.index = sc->sc_outports.master;
mi.un.v.delta = 0;
- if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0) {
+ if (audio_query_devinfo(sc, &mi) == 0) {
au_get_gain(sc, &sc->sc_outports, &gain, &balance);
newgain = gain + mi.un.v.delta;
if (newgain > AUDIO_MAX_GAIN)
@@ -4482,4 +4850,219 @@
return audio_get_props(sc) & AUDIO_PROP_CAPTURE ? true : false;
}
+void
+mix_write(void *arg)
+{
+ struct audio_softc *sc = arg;
+ struct virtual_channel *vc;
+ stream_filter_t *filter;
+ stream_fetcher_t *fetcher;
+ stream_fetcher_t null_fetcher;
+ int cc, cc1, blksize, error;
+ uint8_t *inp;
+
+ vc = sc->sc_vchan[0];
+ blksize = vc->sc_mpr.blksize;
+ cc = blksize;
+
+ if (sc->sc_trigger_started == false)
+ cc *= 2;
+
+ cc1 = cc;
+ if (vc->sc_pustream->inp + cc > vc->sc_pustream->end)
+ cc1 = vc->sc_pustream->end - vc->sc_pustream->inp;
+ memcpy(vc->sc_pustream->inp, sc->sc_pr.s.start, cc1);
+ if (cc1 < cc)
+ memcpy(vc->sc_pustream->start, sc->sc_pr.s.start + cc1, cc - cc1);
+ memset(sc->sc_pr.s.start, 0, cc);
+
+ inp = vc->sc_pustream->inp;
+ vc->sc_pustream->inp = audio_stream_add_inp(vc->sc_pustream, inp, cc);
+
+ if (vc->sc_npfilters > 0) {
+ null_fetcher.fetch_to = null_fetcher_fetch_to;
+ filter = vc->sc_pfilters[0];
+ filter->set_fetcher(filter, &null_fetcher);
+ fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
+ fetcher->fetch_to(sc, fetcher, &vc->sc_mpr.s, cc);
+ }
+
+ if (sc->hw_if->trigger_output && sc->sc_trigger_started == false) {
+ DPRINTF(("%s: call trigger_output\n", __func__));
+ error = sc->hw_if->trigger_output(sc->hw_hdl, vc->sc_mpr.s.start,
+ vc->sc_mpr.s.end, blksize,
+ audio_pint, (void *)sc, &vc->sc_mpr.s.param);
+ } else if (sc->hw_if->start_output) {
+ DPRINTF(("%s: call start_output\n", __func__));
+ error = sc->hw_if->start_output(sc->hw_hdl,
+ __UNCONST(vc->sc_mpr.s.outp), blksize,
+ audio_pint, (void *)sc);
+ if (error) {
+ /* XXX does this really help? */
+ DPRINTF(("audio_mix restart failed: %d\n", error));
+ audio_clear(sc, 0);
+ }
+ }
+ if (sc->sc_trigger_started == false) {
+ vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s, vc->sc_mpr.s.outp,
+ blksize);
+ }
+ sc->sc_trigger_started = true;
+}
+
+void
+mix_func(struct audio_softc *sc, struct audio_ringbuffer *cb, int n)
+{
+ int blksize, cc, cc1, cc2, m, resid;
+ int16_t *orig, *tomix;
+
+ blksize = cb->blksize;
+ resid = blksize;
+ if (sc->sc_trigger_started == false)
+ resid *= 2;
+
+ tomix = __UNCONST(cb->s.outp);
+ orig = (int16_t *)(sc->sc_pr.s.start);
+
+ while (resid > 0) {
+ cc = resid;
+ cc1 = sc->sc_pr.s.end - (uint8_t *)orig;
+ cc2 = cb->s.end - (uint8_t *)tomix;
+ if (cc > cc1)
+ cc = cc1;
+ if (cc > cc2)
+ cc = cc2;
+
+ for (m = 0; m < cc / 2; m++) {
+ orig[m] += (int16_t)((int32_t)(tomix[m] *
+ ((sc->sc_vchan[n]->sc_swvol + 1) * 16)) /
+ (sc->sc_opens * VAUDIOCHANS));
+ }
+
+ if (&orig[m] >= (int16_t *)sc->sc_pr.s.end)
+ orig = (int16_t *)sc->sc_pr.s.start;
+ if (&tomix[m] >= (int16_t *)cb->s.end)
+ tomix = (int16_t *)cb->s.start;
+
+ resid -= cc;
+ }
+}
+
+static int
+audio_set_port(struct audio_softc *sc, mixer_ctrl_t *mc)
+{
+ int n;
+
+ KASSERT(mutex_owned(sc->sc_lock));
+
+ n = (mc->dev - sc->sc_static_nmixer_states) + 1;
+
+ if (mc->dev >= sc->sc_static_nmixer_states && mc->dev < sc->sc_nmixer_states) {
+ sc->sc_vchan[n]->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ return 0;
+ }
+
+ return sc->hw_if->set_port(sc->hw_hdl, mc);
+}
+
+static int
+audio_get_port(struct audio_softc *sc, mixer_ctrl_t *mc)
+{
+ int n;
+
+ KASSERT(mutex_owned(sc->sc_lock));
+
+ n = (mc->dev - sc->sc_static_nmixer_states) + 1;
+
+ if (mc->dev >= sc->sc_static_nmixer_states && mc->dev < sc->sc_nmixer_states) {
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vchan[n]->sc_swvol;
+ return 0;
+ }
+
+ return sc->hw_if->get_port(sc->hw_hdl, mc);
+}
+
+static int
+audio_query_devinfo(struct audio_softc *sc, mixer_devinfo_t *di)
+{
+ char mixLabel[255];
+
+ KASSERT(mutex_owned(sc->sc_lock));
+
+ if (di->index >= sc->sc_static_nmixer_states && di->index <
+ sc->sc_nmixer_states) {
+ di->mixer_class = AUDIO_OUTPUT_CLASS;
+ snprintf(mixLabel, sizeof(mixLabel), AudioNdac"%d", di->index
+ - sc->sc_static_nmixer_states);
+ strcpy(di->label.name, mixLabel);
+ di->type = AUDIO_MIXER_VALUE;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ di->un.v.num_channels = 1;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ return 0;
+ }
+
+ return sc->hw_if->query_devinfo(sc->hw_hdl, di);
+}
+
+static int
+audio_set_params(struct audio_softc *sc, int setmode, int usemode,
+ audio_params_t *play, audio_params_t *rec,
+ stream_filter_list_t *pfil, stream_filter_list_t *rfil, int n)
+{
+
+ struct audio_format norm_format[VAUDIO_NFORMATS] = {
+ { NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
+ 2, AUFMT_STEREO, 1, { 44100 } },
+ };
+ KASSERT(mutex_owned(sc->sc_lock));
+
+ norm_format[0].frequency[0] = sc->sc_vchan[0]->sc_pparams.sample_rate;
+
+ if (n == 0 && sc->hw_if->set_params != NULL) {
+ return sc->hw_if->set_params(sc->hw_hdl, setmode, usemode,
+ play, rec, pfil, rfil);
+ }
+
+ if (auconv_set_converter(norm_format, VAUDIO_NFORMATS, AUMODE_PLAY,
+ play, true, pfil) < 0)
+ return EINVAL;
+ if (auconv_set_converter(norm_format, VAUDIO_NFORMATS, AUMODE_RECORD,
+ rec, true, rfil) < 0)
+ return EINVAL;
+
+ if (pfil->req_size > 0)
+ play = &pfil->filters[0].param;
+
+ return 0;
+}
+
+static int
+audio_query_encoding(struct audio_softc *sc, struct audio_encoding *ae)
+{
+ KASSERT(mutex_owned(sc->sc_lock));
+
+ return auconv_query_encoding(sc->sc_encodings, ae);
+}
+
+void
+audio_play_thread(void *v)
+{
+ struct audio_softc *sc;
+
+ sc = (struct audio_softc *)v;
+
+ mutex_enter(sc->sc_lock);
+ for (;;) {
+ cv_wait_sig(&sc->sc_condvar, sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(sc->sc_lock);
+ kthread_exit(0);
+ }
+
+ audio_mix(sc);
+ }
+
+}
+
#endif /* NAUDIO > 0 */
Index: src/sys/arch/arm/broadcom/bcm2835_vcaudio.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/broadcom/bcm2835_vcaudio.c,v
retrieving revision 1.10
diff -u -r1.10 bcm2835_vcaudio.c
--- src/sys/arch/arm/broadcom/bcm2835_vcaudio.c 28 Jul 2015 21:24:43 -0000 1.10
+++ src/sys/arch/arm/broadcom/bcm2835_vcaudio.c 4 Jun 2016 15:42:49 -0000
@@ -436,7 +436,7 @@
sched = true;
}
- if (sched) {
+ if (sched && sc->sc_pint) {
intr(intrarg);
sc->sc_abytes += sc->sc_pblksize;
cv_signal(&sc->sc_datacv);