xref: /freebsd/sys/dev/sound/dummy.c (revision c824383b269d8abe175ea4751194660716d5600e)
1c15c9315SChristos Margiolis /*-
2c15c9315SChristos Margiolis  * SPDX-License-Identifier: BSD-2-Clause
3c15c9315SChristos Margiolis  *
4*c824383bSChristos Margiolis  * Copyright (c) 2024-2025 The FreeBSD Foundation
5c15c9315SChristos Margiolis  *
6c15c9315SChristos Margiolis  * This software was developed by Christos Margiolis <christos@FreeBSD.org>
7c15c9315SChristos Margiolis  * under sponsorship from the FreeBSD Foundation.
8c15c9315SChristos Margiolis  *
9c15c9315SChristos Margiolis  * Redistribution and use in source and binary forms, with or without
10c15c9315SChristos Margiolis  * modification, are permitted provided that the following conditions
11c15c9315SChristos Margiolis  * are met:
12c15c9315SChristos Margiolis  * 1. Redistributions of source code must retain the above copyright
13c15c9315SChristos Margiolis  *    notice, this list of conditions and the following disclaimer.
14c15c9315SChristos Margiolis  * 2. Redistributions in binary form must reproduce the above copyright
15c15c9315SChristos Margiolis  *    notice, this list of conditions and the following disclaimer in the
16c15c9315SChristos Margiolis  *    documentation and/or other materials provided with the distribution.
17c15c9315SChristos Margiolis  *
18c15c9315SChristos Margiolis  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19c15c9315SChristos Margiolis  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20c15c9315SChristos Margiolis  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21c15c9315SChristos Margiolis  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22c15c9315SChristos Margiolis  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23c15c9315SChristos Margiolis  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24c15c9315SChristos Margiolis  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25c15c9315SChristos Margiolis  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26c15c9315SChristos Margiolis  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27c15c9315SChristos Margiolis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28c15c9315SChristos Margiolis  * SUCH DAMAGE.
29c15c9315SChristos Margiolis  */
30c15c9315SChristos Margiolis 
31c15c9315SChristos Margiolis #include <sys/cdefs.h>
32c15c9315SChristos Margiolis 
33c15c9315SChristos Margiolis #include <sys/param.h>
34c15c9315SChristos Margiolis #include <sys/systm.h>
35c15c9315SChristos Margiolis #include <sys/kernel.h>
36c15c9315SChristos Margiolis #include <sys/bus.h>
37c15c9315SChristos Margiolis 
38c15c9315SChristos Margiolis #ifdef HAVE_KERNEL_OPTION_HEADERS
39c15c9315SChristos Margiolis #include "opt_snd.h"
40c15c9315SChristos Margiolis #endif
41c15c9315SChristos Margiolis 
42c15c9315SChristos Margiolis #include <dev/sound/pcm/sound.h>
43c15c9315SChristos Margiolis #include <mixer_if.h>
44c15c9315SChristos Margiolis 
45c15c9315SChristos Margiolis #define DUMMY_NPCHAN	1
46c15c9315SChristos Margiolis #define DUMMY_NRCHAN	1
47c15c9315SChristos Margiolis #define DUMMY_NCHAN	(DUMMY_NPCHAN + DUMMY_NRCHAN)
48c15c9315SChristos Margiolis 
49c15c9315SChristos Margiolis struct dummy_chan {
50c15c9315SChristos Margiolis 	struct dummy_softc *sc;
51c15c9315SChristos Margiolis 	struct pcm_channel *chan;
52c15c9315SChristos Margiolis 	struct snd_dbuf *buf;
53c15c9315SChristos Margiolis 	struct pcmchan_caps *caps;
54c15c9315SChristos Margiolis 	uint32_t ptr;
55c15c9315SChristos Margiolis 	int dir;
56c15c9315SChristos Margiolis 	int run;
57c15c9315SChristos Margiolis };
58c15c9315SChristos Margiolis 
59c15c9315SChristos Margiolis struct dummy_softc {
60c15c9315SChristos Margiolis 	struct snddev_info info;
61c15c9315SChristos Margiolis 	device_t dev;
62c15c9315SChristos Margiolis 	uint32_t cap_fmts[4];
63c15c9315SChristos Margiolis 	struct pcmchan_caps caps;
64c15c9315SChristos Margiolis 	int chnum;
65c15c9315SChristos Margiolis 	struct dummy_chan chans[DUMMY_NCHAN];
66c15c9315SChristos Margiolis 	struct callout callout;
67c15c9315SChristos Margiolis 	struct mtx *lock;
68c15c9315SChristos Margiolis };
69c15c9315SChristos Margiolis 
705bd08172SChristos Margiolis static bool
dummy_active(struct dummy_softc * sc)715bd08172SChristos Margiolis dummy_active(struct dummy_softc *sc)
725bd08172SChristos Margiolis {
735bd08172SChristos Margiolis 	struct dummy_chan *ch;
745bd08172SChristos Margiolis 	int i;
755bd08172SChristos Margiolis 
765bd08172SChristos Margiolis 	snd_mtxassert(sc->lock);
775bd08172SChristos Margiolis 
785bd08172SChristos Margiolis 	for (i = 0; i < sc->chnum; i++) {
795bd08172SChristos Margiolis 		ch = &sc->chans[i];
805bd08172SChristos Margiolis 		if (ch->run)
815bd08172SChristos Margiolis 			return (true);
825bd08172SChristos Margiolis 	}
835bd08172SChristos Margiolis 
845bd08172SChristos Margiolis 	/* No channel is running at the moment. */
855bd08172SChristos Margiolis 	return (false);
865bd08172SChristos Margiolis }
875bd08172SChristos Margiolis 
88c15c9315SChristos Margiolis static void
dummy_chan_io(void * arg)89c15c9315SChristos Margiolis dummy_chan_io(void *arg)
90c15c9315SChristos Margiolis {
91c15c9315SChristos Margiolis 	struct dummy_softc *sc = arg;
92c15c9315SChristos Margiolis 	struct dummy_chan *ch;
93c15c9315SChristos Margiolis 	int i = 0;
94c15c9315SChristos Margiolis 
955bd08172SChristos Margiolis 	/* Do not reschedule if no channel is running. */
965bd08172SChristos Margiolis 	if (!dummy_active(sc))
975bd08172SChristos Margiolis 		return;
98c15c9315SChristos Margiolis 
99c15c9315SChristos Margiolis 	for (i = 0; i < sc->chnum; i++) {
100c15c9315SChristos Margiolis 		ch = &sc->chans[i];
101c15c9315SChristos Margiolis 		if (!ch->run)
102c15c9315SChristos Margiolis 			continue;
103c15c9315SChristos Margiolis 		if (ch->dir == PCMDIR_PLAY)
104c15c9315SChristos Margiolis 			ch->ptr += sndbuf_getblksz(ch->buf);
105c15c9315SChristos Margiolis 		else
106c15c9315SChristos Margiolis 			sndbuf_fillsilence(ch->buf);
107c15c9315SChristos Margiolis 		snd_mtxunlock(sc->lock);
108c15c9315SChristos Margiolis 		chn_intr(ch->chan);
109c15c9315SChristos Margiolis 		snd_mtxlock(sc->lock);
110c15c9315SChristos Margiolis 	}
111c15c9315SChristos Margiolis 	callout_schedule(&sc->callout, 1);
112c15c9315SChristos Margiolis }
113c15c9315SChristos Margiolis 
114c15c9315SChristos Margiolis static int
dummy_chan_free(kobj_t obj,void * data)115c15c9315SChristos Margiolis dummy_chan_free(kobj_t obj, void *data)
116c15c9315SChristos Margiolis {
117c15c9315SChristos Margiolis 	struct dummy_chan *ch =data;
118c15c9315SChristos Margiolis 	uint8_t *buf;
119c15c9315SChristos Margiolis 
120c15c9315SChristos Margiolis 	buf = sndbuf_getbuf(ch->buf);
121c15c9315SChristos Margiolis 	if (buf != NULL)
122c15c9315SChristos Margiolis 		free(buf, M_DEVBUF);
123c15c9315SChristos Margiolis 
124c15c9315SChristos Margiolis 	return (0);
125c15c9315SChristos Margiolis }
126c15c9315SChristos Margiolis 
127c15c9315SChristos Margiolis static void *
dummy_chan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)128c15c9315SChristos Margiolis dummy_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
129c15c9315SChristos Margiolis     struct pcm_channel *c, int dir)
130c15c9315SChristos Margiolis {
131c15c9315SChristos Margiolis 	struct dummy_softc *sc;
132c15c9315SChristos Margiolis 	struct dummy_chan *ch;
133c15c9315SChristos Margiolis 	uint8_t *buf;
134c15c9315SChristos Margiolis 	size_t bufsz;
135c15c9315SChristos Margiolis 
136c15c9315SChristos Margiolis 	sc = devinfo;
137c15c9315SChristos Margiolis 
138c15c9315SChristos Margiolis 	snd_mtxlock(sc->lock);
139c15c9315SChristos Margiolis 
140c15c9315SChristos Margiolis 	ch = &sc->chans[sc->chnum++];
141c15c9315SChristos Margiolis 	ch->sc = sc;
142c15c9315SChristos Margiolis 	ch->dir = dir;
143c15c9315SChristos Margiolis 	ch->chan = c;
144c15c9315SChristos Margiolis 	ch->buf = b;
145c15c9315SChristos Margiolis 	ch->caps = &sc->caps;
146c15c9315SChristos Margiolis 
147c15c9315SChristos Margiolis 	snd_mtxunlock(sc->lock);
148c15c9315SChristos Margiolis 
149c15c9315SChristos Margiolis 	bufsz = pcm_getbuffersize(sc->dev, 2048, 2048, 65536);
150c15c9315SChristos Margiolis 	buf = malloc(bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
151c15c9315SChristos Margiolis 	if (sndbuf_setup(ch->buf, buf, bufsz) != 0) {
152c15c9315SChristos Margiolis 		dummy_chan_free(obj, ch);
153c15c9315SChristos Margiolis 		return (NULL);
154c15c9315SChristos Margiolis 	}
155c15c9315SChristos Margiolis 
156c15c9315SChristos Margiolis 	return (ch);
157c15c9315SChristos Margiolis }
158c15c9315SChristos Margiolis 
159c15c9315SChristos Margiolis static int
dummy_chan_setformat(kobj_t obj,void * data,uint32_t format)160c15c9315SChristos Margiolis dummy_chan_setformat(kobj_t obj, void *data, uint32_t format)
161c15c9315SChristos Margiolis {
162c15c9315SChristos Margiolis 	struct dummy_chan *ch = data;
163c15c9315SChristos Margiolis 	int i;
164c15c9315SChristos Margiolis 
165c15c9315SChristos Margiolis 	for (i = 0; ch->caps->fmtlist[i]; i++)
166c15c9315SChristos Margiolis 		if (format == ch->caps->fmtlist[i])
167c15c9315SChristos Margiolis 			return (0);
168c15c9315SChristos Margiolis 
169c15c9315SChristos Margiolis 	return (EINVAL);
170c15c9315SChristos Margiolis }
171c15c9315SChristos Margiolis 
172c15c9315SChristos Margiolis static uint32_t
dummy_chan_setspeed(kobj_t obj,void * data,uint32_t speed)173c15c9315SChristos Margiolis dummy_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
174c15c9315SChristos Margiolis {
175c15c9315SChristos Margiolis 	struct dummy_chan *ch = data;
176c15c9315SChristos Margiolis 
177c15c9315SChristos Margiolis 	RANGE(speed, ch->caps->minspeed, ch->caps->maxspeed);
178c15c9315SChristos Margiolis 
179c15c9315SChristos Margiolis 	return (speed);
180c15c9315SChristos Margiolis }
181c15c9315SChristos Margiolis 
182c15c9315SChristos Margiolis static uint32_t
dummy_chan_setblocksize(kobj_t obj,void * data,uint32_t blocksize)183c15c9315SChristos Margiolis dummy_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
184c15c9315SChristos Margiolis {
185c15c9315SChristos Margiolis 	struct dummy_chan *ch = data;
186c15c9315SChristos Margiolis 
187c15c9315SChristos Margiolis 	return (sndbuf_getblksz(ch->buf));
188c15c9315SChristos Margiolis }
189c15c9315SChristos Margiolis 
190c15c9315SChristos Margiolis static int
dummy_chan_trigger(kobj_t obj,void * data,int go)191c15c9315SChristos Margiolis dummy_chan_trigger(kobj_t obj, void *data, int go)
192c15c9315SChristos Margiolis {
193c15c9315SChristos Margiolis 	struct dummy_chan *ch = data;
194c15c9315SChristos Margiolis 	struct dummy_softc *sc = ch->sc;
195c15c9315SChristos Margiolis 
196c15c9315SChristos Margiolis 	snd_mtxlock(sc->lock);
197c15c9315SChristos Margiolis 
198c15c9315SChristos Margiolis 	switch (go) {
199c15c9315SChristos Margiolis 	case PCMTRIG_START:
200c15c9315SChristos Margiolis 		ch->ptr = 0;
201c15c9315SChristos Margiolis 		ch->run = 1;
2025bd08172SChristos Margiolis 		callout_reset(&sc->callout, 1, dummy_chan_io, sc);
203c15c9315SChristos Margiolis 		break;
204c15c9315SChristos Margiolis 	case PCMTRIG_STOP:
205c15c9315SChristos Margiolis 	case PCMTRIG_ABORT:
206c15c9315SChristos Margiolis 		ch->run = 0;
2075bd08172SChristos Margiolis 		/* If all channels are stopped, stop the callout as well. */
2085bd08172SChristos Margiolis 		if (!dummy_active(sc))
209c15c9315SChristos Margiolis 			callout_stop(&sc->callout);
210c15c9315SChristos Margiolis 	default:
211c15c9315SChristos Margiolis 		break;
212c15c9315SChristos Margiolis 	}
213c15c9315SChristos Margiolis 
214c15c9315SChristos Margiolis 	snd_mtxunlock(sc->lock);
215c15c9315SChristos Margiolis 
216c15c9315SChristos Margiolis 	return (0);
217c15c9315SChristos Margiolis }
218c15c9315SChristos Margiolis 
219c15c9315SChristos Margiolis static uint32_t
dummy_chan_getptr(kobj_t obj,void * data)220c15c9315SChristos Margiolis dummy_chan_getptr(kobj_t obj, void *data)
221c15c9315SChristos Margiolis {
222c15c9315SChristos Margiolis 	struct dummy_chan *ch = data;
223c15c9315SChristos Margiolis 
224c15c9315SChristos Margiolis 	return (ch->run ? ch->ptr : 0);
225c15c9315SChristos Margiolis }
226c15c9315SChristos Margiolis 
227c15c9315SChristos Margiolis static struct pcmchan_caps *
dummy_chan_getcaps(kobj_t obj,void * data)228c15c9315SChristos Margiolis dummy_chan_getcaps(kobj_t obj, void *data)
229c15c9315SChristos Margiolis {
230c15c9315SChristos Margiolis 	struct dummy_chan *ch = data;
231c15c9315SChristos Margiolis 
232c15c9315SChristos Margiolis 	return (ch->caps);
233c15c9315SChristos Margiolis }
234c15c9315SChristos Margiolis 
235c15c9315SChristos Margiolis static kobj_method_t dummy_chan_methods[] = {
236c15c9315SChristos Margiolis 	KOBJMETHOD(channel_init,	dummy_chan_init),
237c15c9315SChristos Margiolis 	KOBJMETHOD(channel_free,	dummy_chan_free),
238c15c9315SChristos Margiolis 	KOBJMETHOD(channel_setformat,	dummy_chan_setformat),
239c15c9315SChristos Margiolis 	KOBJMETHOD(channel_setspeed,	dummy_chan_setspeed),
240c15c9315SChristos Margiolis 	KOBJMETHOD(channel_setblocksize,dummy_chan_setblocksize),
241c15c9315SChristos Margiolis 	KOBJMETHOD(channel_trigger,	dummy_chan_trigger),
242c15c9315SChristos Margiolis 	KOBJMETHOD(channel_getptr,	dummy_chan_getptr),
243c15c9315SChristos Margiolis 	KOBJMETHOD(channel_getcaps,	dummy_chan_getcaps),
244c15c9315SChristos Margiolis 	KOBJMETHOD_END
245c15c9315SChristos Margiolis };
246c15c9315SChristos Margiolis 
247c15c9315SChristos Margiolis CHANNEL_DECLARE(dummy_chan);
248c15c9315SChristos Margiolis 
249c15c9315SChristos Margiolis static int
dummy_mixer_init(struct snd_mixer * m)250c15c9315SChristos Margiolis dummy_mixer_init(struct snd_mixer *m)
251c15c9315SChristos Margiolis {
252c15c9315SChristos Margiolis 	struct dummy_softc *sc;
253c15c9315SChristos Margiolis 
254c15c9315SChristos Margiolis 	sc = mix_getdevinfo(m);
255c15c9315SChristos Margiolis 	if (sc == NULL)
256c15c9315SChristos Margiolis 		return (-1);
257c15c9315SChristos Margiolis 
258c15c9315SChristos Margiolis 	pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL);
259c15c9315SChristos Margiolis 	mix_setdevs(m, SOUND_MASK_PCM | SOUND_MASK_VOLUME | SOUND_MASK_RECLEV);
260c15c9315SChristos Margiolis 	mix_setrecdevs(m, SOUND_MASK_RECLEV);
261c15c9315SChristos Margiolis 
262c15c9315SChristos Margiolis 	return (0);
263c15c9315SChristos Margiolis }
264c15c9315SChristos Margiolis 
265c15c9315SChristos Margiolis static int
dummy_mixer_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)266c15c9315SChristos Margiolis dummy_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
267c15c9315SChristos Margiolis {
268c15c9315SChristos Margiolis 	return (0);
269c15c9315SChristos Margiolis }
270c15c9315SChristos Margiolis 
271c15c9315SChristos Margiolis static uint32_t
dummy_mixer_setrecsrc(struct snd_mixer * m,uint32_t src)272c15c9315SChristos Margiolis dummy_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
273c15c9315SChristos Margiolis {
274c15c9315SChristos Margiolis 	return (src == SOUND_MASK_RECLEV ? src : 0);
275c15c9315SChristos Margiolis }
276c15c9315SChristos Margiolis 
277c15c9315SChristos Margiolis static kobj_method_t dummy_mixer_methods[] = {
278c15c9315SChristos Margiolis 	KOBJMETHOD(mixer_init,		dummy_mixer_init),
279c15c9315SChristos Margiolis 	KOBJMETHOD(mixer_set,		dummy_mixer_set),
280c15c9315SChristos Margiolis 	KOBJMETHOD(mixer_setrecsrc,	dummy_mixer_setrecsrc),
281c15c9315SChristos Margiolis 	KOBJMETHOD_END
282c15c9315SChristos Margiolis };
283c15c9315SChristos Margiolis 
284c15c9315SChristos Margiolis MIXER_DECLARE(dummy_mixer);
285c15c9315SChristos Margiolis 
286c15c9315SChristos Margiolis static void
dummy_identify(driver_t * driver,device_t parent)287c15c9315SChristos Margiolis dummy_identify(driver_t *driver, device_t parent)
288c15c9315SChristos Margiolis {
289c15c9315SChristos Margiolis 	if (device_find_child(parent, driver->name, -1) != NULL)
290c15c9315SChristos Margiolis 		return;
291c15c9315SChristos Margiolis 	if (BUS_ADD_CHILD(parent, 0, driver->name, -1) == NULL)
292c15c9315SChristos Margiolis 		device_printf(parent, "add child failed\n");
293c15c9315SChristos Margiolis }
294c15c9315SChristos Margiolis 
295c15c9315SChristos Margiolis static int
dummy_probe(device_t dev)296c15c9315SChristos Margiolis dummy_probe(device_t dev)
297c15c9315SChristos Margiolis {
298c15c9315SChristos Margiolis 	device_set_desc(dev, "Dummy Audio Device");
299c15c9315SChristos Margiolis 
300c15c9315SChristos Margiolis 	return (0);
301c15c9315SChristos Margiolis }
302c15c9315SChristos Margiolis 
303c15c9315SChristos Margiolis static int
dummy_attach(device_t dev)304c15c9315SChristos Margiolis dummy_attach(device_t dev)
305c15c9315SChristos Margiolis {
306c15c9315SChristos Margiolis 	struct dummy_softc *sc;
307c15c9315SChristos Margiolis 	char status[SND_STATUSLEN];
308c15c9315SChristos Margiolis 	int i = 0;
309c15c9315SChristos Margiolis 
310c15c9315SChristos Margiolis 	sc = device_get_softc(dev);
311c15c9315SChristos Margiolis 	sc->dev = dev;
312c15c9315SChristos Margiolis 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_dummy softc");
3135bd08172SChristos Margiolis 	callout_init_mtx(&sc->callout, sc->lock, 0);
314c15c9315SChristos Margiolis 
315c15c9315SChristos Margiolis 	sc->cap_fmts[0] = SND_FORMAT(AFMT_S32_LE, 2, 0);
316c15c9315SChristos Margiolis 	sc->cap_fmts[1] = SND_FORMAT(AFMT_S24_LE, 2, 0);
317c15c9315SChristos Margiolis 	sc->cap_fmts[2] = SND_FORMAT(AFMT_S16_LE, 2, 0);
318c15c9315SChristos Margiolis 	sc->cap_fmts[3] = 0;
319c15c9315SChristos Margiolis 	sc->caps = (struct pcmchan_caps){
320c15c9315SChristos Margiolis 		8000,		/* minspeed */
321c15c9315SChristos Margiolis 		96000,		/* maxspeed */
322c15c9315SChristos Margiolis 		sc->cap_fmts,	/* fmtlist */
323c15c9315SChristos Margiolis 		0,		/* caps */
324c15c9315SChristos Margiolis 	};
325c15c9315SChristos Margiolis 
326c15c9315SChristos Margiolis 	pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
327516a9c02SChristos Margiolis 	pcm_init(dev, sc);
328c15c9315SChristos Margiolis 	for (i = 0; i < DUMMY_NPCHAN; i++)
329c15c9315SChristos Margiolis 		pcm_addchan(dev, PCMDIR_PLAY, &dummy_chan_class, sc);
330c15c9315SChristos Margiolis 	for (i = 0; i < DUMMY_NRCHAN; i++)
331c15c9315SChristos Margiolis 		pcm_addchan(dev, PCMDIR_REC, &dummy_chan_class, sc);
332c15c9315SChristos Margiolis 
333c15c9315SChristos Margiolis 	snprintf(status, SND_STATUSLEN, "on %s",
334c15c9315SChristos Margiolis 	    device_get_nameunit(device_get_parent(dev)));
335516a9c02SChristos Margiolis 	if (pcm_register(dev, status))
336516a9c02SChristos Margiolis 		return (ENXIO);
337c15c9315SChristos Margiolis 	mixer_init(dev, &dummy_mixer_class, sc);
338c15c9315SChristos Margiolis 
339c15c9315SChristos Margiolis 	return (0);
340c15c9315SChristos Margiolis }
341c15c9315SChristos Margiolis 
342c15c9315SChristos Margiolis static int
dummy_detach(device_t dev)343c15c9315SChristos Margiolis dummy_detach(device_t dev)
344c15c9315SChristos Margiolis {
345c15c9315SChristos Margiolis 	struct dummy_softc *sc = device_get_softc(dev);
346c15c9315SChristos Margiolis 	int err;
347c15c9315SChristos Margiolis 
348c15c9315SChristos Margiolis 	err = pcm_unregister(dev);
3495bd08172SChristos Margiolis 	callout_drain(&sc->callout);
350c15c9315SChristos Margiolis 	snd_mtxfree(sc->lock);
351c15c9315SChristos Margiolis 
352c15c9315SChristos Margiolis 	return (err);
353c15c9315SChristos Margiolis }
354c15c9315SChristos Margiolis 
355c15c9315SChristos Margiolis static device_method_t dummy_methods[] = {
356c15c9315SChristos Margiolis 	/* Device interface */
357c15c9315SChristos Margiolis 	DEVMETHOD(device_identify,	dummy_identify),
358c15c9315SChristos Margiolis 	DEVMETHOD(device_probe,		dummy_probe),
359c15c9315SChristos Margiolis 	DEVMETHOD(device_attach,	dummy_attach),
360c15c9315SChristos Margiolis 	DEVMETHOD(device_detach,	dummy_detach),
361c15c9315SChristos Margiolis 	DEVMETHOD_END
362c15c9315SChristos Margiolis };
363c15c9315SChristos Margiolis 
364c15c9315SChristos Margiolis static driver_t dummy_driver = {
365c15c9315SChristos Margiolis 	"pcm",
366c15c9315SChristos Margiolis 	dummy_methods,
367c15c9315SChristos Margiolis 	sizeof(struct dummy_softc),
368c15c9315SChristos Margiolis };
369c15c9315SChristos Margiolis 
370c15c9315SChristos Margiolis DRIVER_MODULE(snd_dummy, nexus, dummy_driver, 0, 0);
371c15c9315SChristos Margiolis MODULE_DEPEND(snd_dummy, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
372c15c9315SChristos Margiolis MODULE_VERSION(snd_dummy, 1);
373