sound.c revision 124617
1264790Sbapt/*
2264790Sbapt * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3319297Sdelphij * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4264790Sbapt * All rights reserved.
5264790Sbapt *
6264790Sbapt * Redistribution and use in source and binary forms, with or without
7264790Sbapt * modification, are permitted provided that the following conditions
8264790Sbapt * are met:
9264790Sbapt * 1. Redistributions of source code must retain the above copyright
10264790Sbapt *    notice, this list of conditions and the following disclaimer.
11264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright
12264790Sbapt *    notice, this list of conditions and the following disclaimer in the
13264790Sbapt *    documentation and/or other materials provided with the distribution.
14264790Sbapt *
15264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18264790Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25264790Sbapt * SUCH DAMAGE.
26264790Sbapt */
27264790Sbapt
28264790Sbapt#include <dev/sound/pcm/sound.h>
29264790Sbapt#include <dev/sound/pcm/vchan.h>
30264790Sbapt#include <sys/sysctl.h>
31264790Sbapt
32264790Sbapt#include "feeder_if.h"
33264790Sbapt
34264790SbaptSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 124617 2004-01-17 10:37:11Z phk $");
35264790Sbapt
36264790Sbaptdevclass_t pcm_devclass;
37264790Sbapt
38264790Sbaptint pcm_veto_load = 1;
39264790Sbapt
40264790Sbapt#ifdef USING_DEVFS
41264790Sbaptint snd_unit = 0;
42264790SbaptTUNABLE_INT("hw.snd.unit", &snd_unit);
43264790Sbapt#endif
44264790Sbapt
45264790Sbaptint snd_maxautovchans = 0;
46264790SbaptTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
47264790Sbapt
48264790SbaptSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
49264790Sbapt
50264790Sbaptstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
51264790Sbapt
52264790Sbaptstruct sysctl_ctx_list *
53264790Sbaptsnd_sysctl_tree(device_t dev)
54264790Sbapt{
55264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
56264790Sbapt
57264790Sbapt	return &d->sysctl_tree;
58264790Sbapt}
59264790Sbapt
60264790Sbaptstruct sysctl_oid *
61264790Sbaptsnd_sysctl_tree_top(device_t dev)
62264790Sbapt{
63264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
64264790Sbapt
65264790Sbapt	return d->sysctl_tree_top;
66264790Sbapt}
67264790Sbapt
68264790Sbaptvoid *
69264790Sbaptsnd_mtxcreate(const char *desc, const char *type)
70264790Sbapt{
71264790Sbapt#ifdef USING_MUTEX
72264790Sbapt	struct mtx *m;
73264790Sbapt
74264790Sbapt	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
75264790Sbapt	if (m == NULL)
76264790Sbapt		return NULL;
77264790Sbapt	mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE);
78264790Sbapt	return m;
79264790Sbapt#else
80264790Sbapt	return (void *)0xcafebabe;
81264790Sbapt#endif
82264790Sbapt}
83264790Sbapt
84264790Sbaptvoid
85264790Sbaptsnd_mtxfree(void *m)
86264790Sbapt{
87264790Sbapt#ifdef USING_MUTEX
88264790Sbapt	struct mtx *mtx = m;
89264790Sbapt
90264790Sbapt	/* mtx_assert(mtx, MA_OWNED); */
91264790Sbapt	mtx_destroy(mtx);
92264790Sbapt	free(mtx, M_DEVBUF);
93264790Sbapt#endif
94264790Sbapt}
95264790Sbapt
96264790Sbaptvoid
97264790Sbaptsnd_mtxassert(void *m)
98264790Sbapt{
99264790Sbapt#ifdef USING_MUTEX
100264790Sbapt#ifdef INVARIANTS
101264790Sbapt	struct mtx *mtx = m;
102264790Sbapt
103264790Sbapt	mtx_assert(mtx, MA_OWNED);
104264790Sbapt#endif
105264790Sbapt#endif
106264790Sbapt}
107264790Sbapt/*
108264790Sbaptvoid
109264790Sbaptsnd_mtxlock(void *m)
110264790Sbapt{
111264790Sbapt#ifdef USING_MUTEX
112264790Sbapt	struct mtx *mtx = m;
113264790Sbapt
114264790Sbapt	mtx_lock(mtx);
115264790Sbapt#endif
116264790Sbapt}
117264790Sbapt
118264790Sbaptvoid
119264790Sbaptsnd_mtxunlock(void *m)
120264790Sbapt{
121264790Sbapt#ifdef USING_MUTEX
122264790Sbapt	struct mtx *mtx = m;
123264790Sbapt
124264790Sbapt	mtx_unlock(mtx);
125264790Sbapt#endif
126264790Sbapt}
127264790Sbapt*/
128264790Sbaptint
129264790Sbaptsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
130264790Sbapt{
131264790Sbapt#ifdef USING_MUTEX
132264790Sbapt	flags &= INTR_MPSAFE;
133264790Sbapt	flags |= INTR_TYPE_AV;
134264790Sbapt#else
135264790Sbapt	flags = INTR_TYPE_AV;
136264790Sbapt#endif
137264790Sbapt	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
138264790Sbapt}
139264790Sbapt
140264790Sbapt#ifndef	PCM_DEBUG_MTX
141264790Sbaptvoid
142264790Sbaptpcm_lock(struct snddev_info *d)
143264790Sbapt{
144264790Sbapt	snd_mtxlock(d->lock);
145264790Sbapt}
146264790Sbapt
147319297Sdelphijvoid
148264790Sbaptpcm_unlock(struct snddev_info *d)
149264790Sbapt{
150264790Sbapt	snd_mtxunlock(d->lock);
151264790Sbapt}
152264790Sbapt#endif
153264790Sbapt
154264790Sbaptstruct pcm_channel *
155264790Sbaptpcm_getfakechan(struct snddev_info *d)
156264790Sbapt{
157264790Sbapt	return d->fakechan;
158264790Sbapt}
159264790Sbapt
160264790Sbapt/* return a locked channel */
161264790Sbaptstruct pcm_channel *
162264790Sbaptpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
163264790Sbapt{
164264790Sbapt	struct pcm_channel *c;
165264790Sbapt    	struct snddev_channel *sce;
166264790Sbapt	int err;
167264790Sbapt
168264790Sbapt	snd_mtxassert(d->lock);
169264790Sbapt
170264790Sbapt	/* scan for a free channel */
171264790Sbapt	SLIST_FOREACH(sce, &d->channels, link) {
172264790Sbapt		c = sce->channel;
173264790Sbapt		CHN_LOCK(c);
174264790Sbapt		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
175264790Sbapt			if (chnum == -1 || c->num == chnum) {
176264790Sbapt				c->flags |= CHN_F_BUSY;
177264790Sbapt				c->pid = pid;
178264790Sbapt				return c;
179264790Sbapt			}
180264790Sbapt		}
181264790Sbapt		CHN_UNLOCK(c);
182264790Sbapt	}
183264790Sbapt
184264790Sbapt	/* no channel available */
185264790Sbapt	if (direction == PCMDIR_PLAY) {
186264790Sbapt		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
187264790Sbapt			/* try to create a vchan */
188264790Sbapt			SLIST_FOREACH(sce, &d->channels, link) {
189264790Sbapt				c = sce->channel;
190264790Sbapt				if (!SLIST_EMPTY(&c->children)) {
191264790Sbapt					err = vchan_create(c);
192264790Sbapt					if (!err)
193264790Sbapt						return pcm_chnalloc(d, direction, pid, -1);
194264790Sbapt					else
195264790Sbapt						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
196264790Sbapt				}
197264790Sbapt			}
198264790Sbapt		}
199264790Sbapt	}
200264790Sbapt
201264790Sbapt	return NULL;
202264790Sbapt}
203264790Sbapt
204264790Sbapt/* release a locked channel and unlock it */
205264790Sbaptint
206264790Sbaptpcm_chnrelease(struct pcm_channel *c)
207264790Sbapt{
208319297Sdelphij	CHN_LOCKASSERT(c);
209264790Sbapt	c->flags &= ~CHN_F_BUSY;
210264790Sbapt	c->pid = -1;
211264790Sbapt	CHN_UNLOCK(c);
212264790Sbapt	return 0;
213319297Sdelphij}
214264790Sbapt
215264790Sbaptint
216264790Sbaptpcm_chnref(struct pcm_channel *c, int ref)
217264790Sbapt{
218264790Sbapt	int r;
219264790Sbapt
220264790Sbapt	CHN_LOCKASSERT(c);
221264790Sbapt	c->refcount += ref;
222264790Sbapt	r = c->refcount;
223264790Sbapt	return r;
224264790Sbapt}
225264790Sbapt
226264790Sbaptint
227264790Sbaptpcm_inprog(struct snddev_info *d, int delta)
228264790Sbapt{
229264790Sbapt	int r;
230264790Sbapt
231264790Sbapt	if (delta == 0)
232264790Sbapt		return d->inprog;
233264790Sbapt
234264790Sbapt	/* backtrace(); */
235264790Sbapt	pcm_lock(d);
236264790Sbapt	d->inprog += delta;
237264790Sbapt	r = d->inprog;
238264790Sbapt	pcm_unlock(d);
239264790Sbapt	return r;
240264790Sbapt}
241264790Sbapt
242264790Sbaptstatic void
243272655Sbaptpcm_setmaxautovchans(struct snddev_info *d, int num)
244272655Sbapt{
245272655Sbapt	struct pcm_channel *c;
246272655Sbapt    	struct snddev_channel *sce;
247272655Sbapt	int err, done;
248264790Sbapt
249264790Sbapt	if (num > 0 && d->vchancount == 0) {
250264790Sbapt		SLIST_FOREACH(sce, &d->channels, link) {
251264790Sbapt			c = sce->channel;
252264790Sbapt			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
253264790Sbapt				c->flags |= CHN_F_BUSY;
254264790Sbapt				err = vchan_create(c);
255264790Sbapt				if (err) {
256264790Sbapt					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
257264790Sbapt					c->flags &= ~CHN_F_BUSY;
258264790Sbapt				}
259264790Sbapt				return;
260264790Sbapt			}
261264790Sbapt		}
262264790Sbapt	}
263264790Sbapt	if (num == 0 && d->vchancount > 0) {
264264790Sbapt		done = 0;
265264790Sbapt		while (!done) {
266264790Sbapt			done = 1;
267264790Sbapt			SLIST_FOREACH(sce, &d->channels, link) {
268264790Sbapt				c = sce->channel;
269264790Sbapt				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
270264790Sbapt					done = 0;
271264790Sbapt					snd_mtxlock(d->lock);
272264790Sbapt					err = vchan_destroy(c);
273264790Sbapt					snd_mtxunlock(d->lock);
274264790Sbapt					if (err)
275264790Sbapt						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
276264790Sbapt					break;		/* restart */
277264790Sbapt				}
278264790Sbapt			}
279264790Sbapt		}
280264790Sbapt	}
281264790Sbapt}
282264790Sbapt
283264790Sbapt#ifdef USING_DEVFS
284264790Sbaptstatic int
285264790Sbaptsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
286264790Sbapt{
287264790Sbapt	struct snddev_info *d;
288264790Sbapt	int error, unit;
289264790Sbapt
290264790Sbapt	unit = snd_unit;
291264790Sbapt	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
292264790Sbapt	if (error == 0 && req->newptr != NULL) {
293264790Sbapt		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
294264790Sbapt			return EINVAL;
295264790Sbapt		d = devclass_get_softc(pcm_devclass, unit);
296264790Sbapt		if (d == NULL || SLIST_EMPTY(&d->channels))
297264790Sbapt			return EINVAL;
298264790Sbapt		snd_unit = unit;
299264790Sbapt	}
300264790Sbapt	return (error);
301264790Sbapt}
302264790SbaptSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
303264790Sbapt            0, sizeof(int), sysctl_hw_snd_unit, "I", "");
304264790Sbapt#endif
305264790Sbapt
306264790Sbaptstatic int
307264790Sbaptsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
308264790Sbapt{
309264790Sbapt	struct snddev_info *d;
310264790Sbapt	int i, v, error;
311264790Sbapt
312264790Sbapt	v = snd_maxautovchans;
313264790Sbapt	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
314264790Sbapt	if (error == 0 && req->newptr != NULL) {
315264790Sbapt		if (v < 0 || v >= SND_MAXVCHANS)
316264790Sbapt			return EINVAL;
317264790Sbapt		if (v != snd_maxautovchans) {
318264790Sbapt			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
319264790Sbapt				d = devclass_get_softc(pcm_devclass, i);
320264790Sbapt				if (!d)
321264790Sbapt					continue;
322264790Sbapt				pcm_setmaxautovchans(d, v);
323264790Sbapt			}
324264790Sbapt		}
325264790Sbapt		snd_maxautovchans = v;
326264790Sbapt	}
327264790Sbapt	return (error);
328264790Sbapt}
329264790SbaptSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
330264790Sbapt            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
331264790Sbapt
332264790Sbaptstruct pcm_channel *
333264790Sbaptpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
334264790Sbapt{
335264790Sbapt	struct pcm_channel *ch;
336264790Sbapt	char *dirs;
337264790Sbapt    	int err, *pnum;
338264790Sbapt
339264790Sbapt	switch(dir) {
340264790Sbapt	case PCMDIR_PLAY:
341264790Sbapt		dirs = "play";
342264790Sbapt		pnum = &d->playcount;
343264790Sbapt		break;
344264790Sbapt
345319297Sdelphij	case PCMDIR_REC:
346319297Sdelphij		dirs = "record";
347319297Sdelphij		pnum = &d->reccount;
348264790Sbapt		break;
349264790Sbapt
350264790Sbapt	case PCMDIR_VIRTUAL:
351264790Sbapt		dirs = "virtual";
352264790Sbapt		dir = PCMDIR_PLAY;
353264790Sbapt		pnum = &d->vchancount;
354264790Sbapt		break;
355264790Sbapt
356264790Sbapt	default:
357264790Sbapt		return NULL;
358264790Sbapt	}
359264790Sbapt
360264790Sbapt	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
361264790Sbapt	if (!ch)
362264790Sbapt		return NULL;
363264790Sbapt
364264790Sbapt	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
365264790Sbapt	if (!ch->methods) {
366264790Sbapt		free(ch, M_DEVBUF);
367264790Sbapt
368264790Sbapt		return NULL;
369264790Sbapt	}
370264790Sbapt
371264790Sbapt	snd_mtxlock(d->lock);
372264790Sbapt	ch->num = (*pnum)++;
373264790Sbapt	snd_mtxunlock(d->lock);
374264790Sbapt
375264790Sbapt	ch->pid = -1;
376264790Sbapt	ch->parentsnddev = d;
377264790Sbapt	ch->parentchannel = parent;
378264790Sbapt	ch->dev = d->dev;
379264790Sbapt	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
380264790Sbapt
381264790Sbapt	err = chn_init(ch, devinfo, dir);
382264790Sbapt	if (err) {
383264790Sbapt		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
384264790Sbapt		kobj_delete(ch->methods, M_DEVBUF);
385264790Sbapt		free(ch, M_DEVBUF);
386264790Sbapt		snd_mtxlock(d->lock);
387264790Sbapt		(*pnum)--;
388264790Sbapt		snd_mtxunlock(d->lock);
389264790Sbapt
390264790Sbapt		return NULL;
391264790Sbapt	}
392264790Sbapt
393264790Sbapt	return ch;
394264790Sbapt}
395264790Sbapt
396264790Sbaptint
397264790Sbaptpcm_chn_destroy(struct pcm_channel *ch)
398264790Sbapt{
399264790Sbapt	struct snddev_info *d;
400264790Sbapt	int err;
401264790Sbapt
402264790Sbapt	d = ch->parentsnddev;
403264790Sbapt	err = chn_kill(ch);
404264790Sbapt	if (err) {
405319297Sdelphij		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
406264790Sbapt		return err;
407319297Sdelphij	}
408264790Sbapt
409264790Sbapt	kobj_delete(ch->methods, M_DEVBUF);
410264790Sbapt	free(ch, M_DEVBUF);
411264790Sbapt
412264790Sbapt	return 0;
413319297Sdelphij}
414264790Sbapt
415264790Sbaptint
416264790Sbaptpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
417264790Sbapt{
418264790Sbapt    	struct snddev_channel *sce, *tmp, *after;
419264790Sbapt    	int unit = device_get_unit(d->dev);
420264790Sbapt	int x = -1;
421264790Sbapt
422319297Sdelphij	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
423264790Sbapt	if (!sce) {
424264790Sbapt		return ENOMEM;
425319297Sdelphij	}
426319297Sdelphij
427264790Sbapt	snd_mtxlock(d->lock);
428264790Sbapt	sce->channel = ch;
429264790Sbapt	if (SLIST_EMPTY(&d->channels)) {
430264790Sbapt		SLIST_INSERT_HEAD(&d->channels, sce, link);
431264790Sbapt	} else {
432264790Sbapt		after = NULL;
433319297Sdelphij		SLIST_FOREACH(tmp, &d->channels, link) {
434264790Sbapt			after = tmp;
435264790Sbapt		}
436264790Sbapt		SLIST_INSERT_AFTER(after, sce, link);
437264790Sbapt	}
438264790Sbapt	if (mkdev)
439264790Sbapt		x = d->devcount++;
440264790Sbapt	snd_mtxunlock(d->lock);
441264790Sbapt
442264790Sbapt	if (mkdev) {
443264790Sbapt		dsp_register(unit, x);
444264790Sbapt		if (ch->direction == PCMDIR_REC)
445264790Sbapt			dsp_registerrec(unit, ch->num);
446264790Sbapt	}
447319297Sdelphij
448264790Sbapt	return 0;
449264790Sbapt}
450264790Sbapt
451264790Sbaptint
452264790Sbaptpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
453264790Sbapt{
454264790Sbapt    	struct snddev_channel *sce;
455264790Sbapt    	int unit = device_get_unit(d->dev);
456264790Sbapt#if 0
457264790Sbapt	int ourlock;
458264790Sbapt
459264790Sbapt	ourlock = 0;
460264790Sbapt	if (!mtx_owned(d->lock)) {
461264790Sbapt		snd_mtxlock(d->lock);
462264790Sbapt		ourlock = 1;
463264790Sbapt	}
464264790Sbapt#endif
465264790Sbapt
466264790Sbapt	SLIST_FOREACH(sce, &d->channels, link) {
467264790Sbapt		if (sce->channel == ch)
468264790Sbapt			goto gotit;
469264790Sbapt	}
470264790Sbapt#if 0
471264790Sbapt	if (ourlock)
472264790Sbapt		snd_mtxunlock(d->lock);
473264790Sbapt#endif
474264790Sbapt	return EINVAL;
475264790Sbaptgotit:
476264790Sbapt	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
477264790Sbapt	if (rmdev) {
478264790Sbapt		dsp_unregister(unit, --d->devcount);
479264790Sbapt		if (ch->direction == PCMDIR_REC)
480264790Sbapt			dsp_unregisterrec(unit, ch->num);
481264790Sbapt	}
482264790Sbapt
483264790Sbapt    	if (ch->direction == PCMDIR_REC)
484264790Sbapt		d->reccount--;
485264790Sbapt	else if (ch->flags & CHN_F_VIRTUAL)
486264790Sbapt		d->vchancount--;
487264790Sbapt	else
488264790Sbapt		d->playcount--;
489264790Sbapt
490264790Sbapt#if 0
491264790Sbapt	if (ourlock)
492264790Sbapt		snd_mtxunlock(d->lock);
493264790Sbapt#endif
494264790Sbapt	free(sce, M_DEVBUF);
495264790Sbapt
496264790Sbapt	return 0;
497264790Sbapt}
498264790Sbapt
499264790Sbaptint
500264790Sbaptpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
501264790Sbapt{
502264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
503264790Sbapt	struct pcm_channel *ch;
504264790Sbapt    	int err;
505264790Sbapt
506264790Sbapt	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
507264790Sbapt	if (!ch) {
508264790Sbapt		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
509264790Sbapt		return ENODEV;
510264790Sbapt	}
511319297Sdelphij
512264790Sbapt	err = pcm_chn_add(d, ch, 1);
513264790Sbapt	if (err) {
514264790Sbapt		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
515264790Sbapt		snd_mtxunlock(d->lock);
516264790Sbapt		pcm_chn_destroy(ch);
517264790Sbapt		return err;
518264790Sbapt	}
519264790Sbapt
520264790Sbapt	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
521264790Sbapt	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
522264790Sbapt		ch->flags |= CHN_F_BUSY;
523264790Sbapt		err = vchan_create(ch);
524264790Sbapt		if (err) {
525264790Sbapt			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
526264790Sbapt			ch->flags &= ~CHN_F_BUSY;
527264790Sbapt		}
528264790Sbapt	}
529264790Sbapt
530264790Sbapt	return err;
531264790Sbapt}
532264790Sbapt
533264790Sbaptstatic int
534264790Sbaptpcm_killchan(device_t dev)
535264790Sbapt{
536264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
537264790Sbapt    	struct snddev_channel *sce;
538264790Sbapt	struct pcm_channel *ch;
539264790Sbapt	int error = 0;
540264790Sbapt
541264790Sbapt	sce = SLIST_FIRST(&d->channels);
542264790Sbapt	ch = sce->channel;
543264790Sbapt
544264790Sbapt	error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children));
545264790Sbapt	if (error)
546264790Sbapt		return (error);
547264790Sbapt	return (pcm_chn_destroy(ch));
548264790Sbapt}
549264790Sbapt
550264790Sbaptint
551264790Sbaptpcm_setstatus(device_t dev, char *str)
552264790Sbapt{
553264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
554264790Sbapt
555264790Sbapt	snd_mtxlock(d->lock);
556264790Sbapt	strncpy(d->status, str, SND_STATUSLEN);
557264790Sbapt	snd_mtxunlock(d->lock);
558264790Sbapt	return 0;
559264790Sbapt}
560264790Sbapt
561264790Sbaptu_int32_t
562264790Sbaptpcm_getflags(device_t dev)
563264790Sbapt{
564264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
565264790Sbapt
566264790Sbapt	return d->flags;
567264790Sbapt}
568264790Sbapt
569264790Sbaptvoid
570264790Sbaptpcm_setflags(device_t dev, u_int32_t val)
571319297Sdelphij{
572319297Sdelphij    	struct snddev_info *d = device_get_softc(dev);
573264790Sbapt
574264790Sbapt	d->flags = val;
575264790Sbapt}
576264790Sbapt
577264790Sbaptvoid *
578264790Sbaptpcm_getdevinfo(device_t dev)
579264790Sbapt{
580264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
581264790Sbapt
582264790Sbapt	return d->devinfo;
583264790Sbapt}
584264790Sbapt
585264790Sbaptunsigned int
586264790Sbaptpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
587264790Sbapt{
588264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
589264790Sbapt	int sz, x;
590264790Sbapt
591264790Sbapt	sz = 0;
592264790Sbapt	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
593264790Sbapt		x = sz;
594264790Sbapt		RANGE(sz, min, max);
595264790Sbapt		if (x != sz)
596264790Sbapt			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
597264790Sbapt		x = min;
598264790Sbapt		while (x < sz)
599264790Sbapt			x <<= 1;
600264790Sbapt		if (x > sz)
601264790Sbapt			x >>= 1;
602264790Sbapt		if (x != sz) {
603264790Sbapt			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
604264790Sbapt			sz = x;
605264790Sbapt		}
606264790Sbapt	} else {
607264790Sbapt		sz = deflt;
608264790Sbapt	}
609264790Sbapt
610264790Sbapt	d->bufsz = sz;
611264790Sbapt
612272655Sbapt	return sz;
613272655Sbapt}
614264790Sbapt
615264790Sbaptint
616319297Sdelphijpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
617319297Sdelphij{
618264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
619319297Sdelphij
620264790Sbapt	if (pcm_veto_load) {
621264790Sbapt		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
622264790Sbapt
623264790Sbapt		return EINVAL;
624264790Sbapt	}
625264790Sbapt
626264790Sbapt	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
627264790Sbapt
628264790Sbapt	d->flags = 0;
629319297Sdelphij	d->dev = dev;
630264790Sbapt	d->devinfo = devinfo;
631264790Sbapt	d->devcount = 0;
632264790Sbapt	d->reccount = 0;
633264790Sbapt	d->playcount = 0;
634264790Sbapt	d->vchancount = 0;
635264790Sbapt	d->inprog = 0;
636264790Sbapt
637264790Sbapt	SLIST_INIT(&d->channels);
638264790Sbapt	SLIST_INIT(&d->channels);
639264790Sbapt
640264790Sbapt	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
641264790Sbapt		d->flags |= SD_F_SIMPLEX;
642264790Sbapt
643264790Sbapt	d->fakechan = fkchan_setup(dev);
644264790Sbapt	chn_init(d->fakechan, NULL, 0);
645264790Sbapt
646264790Sbapt#ifdef SND_DYNSYSCTL
647264790Sbapt	sysctl_ctx_init(&d->sysctl_tree);
648264790Sbapt	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
649319297Sdelphij				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
650264790Sbapt				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
651264790Sbapt	if (d->sysctl_tree_top == NULL) {
652264790Sbapt		sysctl_ctx_free(&d->sysctl_tree);
653264790Sbapt		goto no;
654264790Sbapt	}
655264790Sbapt	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
656264790Sbapt            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
657264790Sbapt#endif
658264790Sbapt	if (numplay > 0)
659264790Sbapt		vchan_initsys(dev);
660264790Sbapt	if (numplay == 1)
661264790Sbapt		d->flags |= SD_F_AUTOVCHAN;
662264790Sbapt
663264790Sbapt	sndstat_register(dev, d->status, sndstat_prepare_pcm);
664264790Sbapt    	return 0;
665264790Sbaptno:
666264790Sbapt	snd_mtxfree(d->lock);
667264790Sbapt	return ENXIO;
668264790Sbapt}
669264790Sbapt
670264790Sbaptint
671264790Sbaptpcm_unregister(device_t dev)
672264790Sbapt{
673264790Sbapt    	struct snddev_info *d = device_get_softc(dev);
674264790Sbapt    	struct snddev_channel *sce;
675264790Sbapt	struct pcm_channel *ch;
676264790Sbapt
677264790Sbapt	snd_mtxlock(d->lock);
678264790Sbapt	if (d->inprog) {
679264790Sbapt		device_printf(dev, "unregister: operation in progress\n");
680264790Sbapt		snd_mtxunlock(d->lock);
681264790Sbapt		return EBUSY;
682264790Sbapt	}
683264790Sbapt	if (sndstat_busy() != 0) {
684264790Sbapt		device_printf(dev, "unregister: sndstat busy\n");
685264790Sbapt		snd_mtxunlock(d->lock);
686264790Sbapt		return EBUSY;
687264790Sbapt	}
688264790Sbapt	SLIST_FOREACH(sce, &d->channels, link) {
689264790Sbapt		ch = sce->channel;
690264790Sbapt		if (ch->refcount > 0) {
691264790Sbapt			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
692264790Sbapt			snd_mtxunlock(d->lock);
693264790Sbapt			return EBUSY;
694264790Sbapt		}
695264790Sbapt	}
696264790Sbapt	if (mixer_uninit(dev)) {
697264790Sbapt		device_printf(dev, "unregister: mixer busy\n");
698264790Sbapt		snd_mtxunlock(d->lock);
699264790Sbapt		return EBUSY;
700264790Sbapt	}
701264790Sbapt
702264790Sbapt#ifdef SND_DYNSYSCTL
703264790Sbapt	d->sysctl_tree_top = NULL;
704264790Sbapt	sysctl_ctx_free(&d->sysctl_tree);
705264790Sbapt#endif
706264790Sbapt	while (!SLIST_EMPTY(&d->channels))
707264790Sbapt		pcm_killchan(dev);
708319297Sdelphij
709264790Sbapt	chn_kill(d->fakechan);
710272655Sbapt	fkchan_kill(d->fakechan);
711264790Sbapt
712264790Sbapt	sndstat_unregister(dev);
713272655Sbapt	snd_mtxunlock(d->lock);
714264790Sbapt	snd_mtxfree(d->lock);
715264790Sbapt	return 0;
716264790Sbapt}
717264790Sbapt
718264790Sbaptint
719264790Sbaptpcm_regdevt(dev_t dev, unsigned unit, unsigned type, unsigned channel)
720264790Sbapt{
721264790Sbapt    	struct snddev_info *d;
722264790Sbapt	struct snddev_devt *dt;
723264790Sbapt
724264790Sbapt	d = devclass_get_softc(pcm_devclass, unit);
725264790Sbapt	KASSERT((d != NULL), ("bad d"));
726264790Sbapt	KASSERT((dev != NULL), ("bad dev"));
727264790Sbapt
728264790Sbapt	dt = malloc(sizeof(*dt), M_DEVBUF, M_ZERO | M_WAITOK);
729264790Sbapt	if (dt == NULL)
730319297Sdelphij		return ENOMEM;
731264790Sbapt	dt->dev = dev;
732264790Sbapt	dt->type = type;
733264790Sbapt	dt->channel = channel;
734264790Sbapt
735264790Sbapt	snd_mtxlock(d->lock);
736264790Sbapt	SLIST_INSERT_HEAD(&d->devs, dt, link);
737264790Sbapt	snd_mtxunlock(d->lock);
738264790Sbapt
739264790Sbapt	return 0;
740264790Sbapt}
741264790Sbapt
742264790Sbaptdev_t
743264790Sbaptpcm_getdevt(unsigned unit, unsigned type, unsigned channel)
744264790Sbapt{
745264790Sbapt    	struct snddev_info *d;
746264790Sbapt	struct snddev_devt *dt;
747264790Sbapt
748264790Sbapt	d = devclass_get_softc(pcm_devclass, unit);
749264790Sbapt	KASSERT((d != NULL), ("bad d"));
750264790Sbapt
751264790Sbapt#if 0
752264790Sbapt	snd_mtxlock(d->lock);
753264790Sbapt#endif
754319297Sdelphij	SLIST_FOREACH(dt, &d->devs, link) {
755264790Sbapt		if ((dt->type == type) && (dt->channel == channel))
756264790Sbapt			return dt->dev;
757264790Sbapt	}
758264790Sbapt#if 0
759264790Sbapt	snd_mtxunlock(d->lock);
760264790Sbapt#endif
761264790Sbapt
762264790Sbapt	return NULL;
763264790Sbapt}
764264790Sbapt
765264790Sbaptint
766264790Sbaptpcm_unregdevt(unsigned unit, unsigned type, unsigned channel)
767264790Sbapt{
768272655Sbapt    	struct snddev_info *d;
769264790Sbapt	struct snddev_devt *dt;
770264790Sbapt
771264790Sbapt	d = devclass_get_softc(pcm_devclass, unit);
772264790Sbapt	KASSERT((d != NULL), ("bad d"));
773264790Sbapt
774264790Sbapt#if 0
775264790Sbapt       	snd_mtxlock(d->lock);
776264790Sbapt#endif
777264790Sbapt	SLIST_FOREACH(dt, &d->devs, link) {
778264790Sbapt		if ((dt->type == type) && (dt->channel == channel)) {
779264790Sbapt			SLIST_REMOVE(&d->devs, dt, snddev_devt, link);
780264790Sbapt			free(dt, M_DEVBUF);
781264790Sbapt#if 0
782264790Sbapt			snd_mtxunlock(d->lock);
783264790Sbapt#endif
784264790Sbapt			return 0;
785264790Sbapt		}
786264790Sbapt	}
787264790Sbapt#if 0
788264790Sbapt	snd_mtxunlock(d->lock);
789319297Sdelphij#endif
790264790Sbapt
791264790Sbapt	return ENOENT;
792264790Sbapt}
793264790Sbapt
794264790Sbapt/************************************************************************/
795264790Sbapt
796264790Sbaptstatic int
797264790Sbaptsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
798264790Sbapt{
799264790Sbapt    	struct snddev_info *d;
800264790Sbapt    	struct snddev_channel *sce;
801264790Sbapt	struct pcm_channel *c;
802264790Sbapt	struct pcm_feeder *f;
803264790Sbapt    	int pc, rc, vc;
804264790Sbapt
805264790Sbapt	if (verbose < 1)
806264790Sbapt		return 0;
807264790Sbapt
808264790Sbapt	d = device_get_softc(dev);
809264790Sbapt	if (!d)
810264790Sbapt		return ENXIO;
811264790Sbapt
812264790Sbapt	snd_mtxlock(d->lock);
813264790Sbapt	if (!SLIST_EMPTY(&d->channels)) {
814264790Sbapt		pc = rc = vc = 0;
815264790Sbapt		SLIST_FOREACH(sce, &d->channels, link) {
816264790Sbapt			c = sce->channel;
817264790Sbapt			if (c->direction == PCMDIR_PLAY) {
818264790Sbapt				if (c->flags & CHN_F_VIRTUAL)
819264790Sbapt					vc++;
820264790Sbapt				else
821264790Sbapt					pc++;
822264790Sbapt			} else
823264790Sbapt				rc++;
824264790Sbapt		}
825264790Sbapt		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
826264790Sbapt				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
827264790Sbapt#ifdef USING_DEVFS
828264790Sbapt				(device_get_unit(dev) == snd_unit)? " default" : ""
829264790Sbapt#else
830264790Sbapt				""
831264790Sbapt#endif
832264790Sbapt				);
833264790Sbapt
834264790Sbapt		if (verbose <= 1) {
835319297Sdelphij			snd_mtxunlock(d->lock);
836264790Sbapt			return 0;
837264790Sbapt		}
838319297Sdelphij
839319297Sdelphij		SLIST_FOREACH(sce, &d->channels, link) {
840319297Sdelphij			c = sce->channel;
841319297Sdelphij			sbuf_printf(s, "\n\t");
842319297Sdelphij
843319297Sdelphij			/* it would be better to indent child channels */
844319297Sdelphij			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
845319297Sdelphij			sbuf_printf(s, "spd %d", c->speed);
846319297Sdelphij			if (c->speed != sndbuf_getspd(c->bufhard))
847264790Sbapt				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
848264790Sbapt			sbuf_printf(s, ", fmt 0x%08x", c->format);
849264790Sbapt			if (c->format != sndbuf_getfmt(c->bufhard))
850264790Sbapt				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
851264790Sbapt			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
852264790Sbapt			if (c->pid != -1)
853264790Sbapt				sbuf_printf(s, ", pid %d", c->pid);
854264790Sbapt			sbuf_printf(s, "\n\t");
855264790Sbapt
856264790Sbapt			if (c->bufhard != NULL && c->bufsoft != NULL) {
857264790Sbapt				sbuf_printf(s, "interrupts %d, ", c->interrupts);
858264790Sbapt				if (c->direction == PCMDIR_REC)
859264790Sbapt					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
860264790Sbapt						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
861264790Sbapt				else
862264790Sbapt					sbuf_printf(s, "underruns %d, ready %d",
863264790Sbapt						c->xruns, sndbuf_getready(c->bufsoft));
864264790Sbapt				sbuf_printf(s, "\n\t");
865264790Sbapt			}
866264790Sbapt
867264790Sbapt			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
868264790Sbapt			sbuf_printf(s, " -> ");
869264790Sbapt			f = c->feeder;
870264790Sbapt			while (f->source != NULL)
871319297Sdelphij				f = f->source;
872264790Sbapt			while (f != NULL) {
873272655Sbapt				sbuf_printf(s, "%s", f->class->name);
874264790Sbapt				if (f->desc->type == FEEDER_FMT)
875264790Sbapt					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
876272655Sbapt				if (f->desc->type == FEEDER_RATE)
877264790Sbapt					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
878272655Sbapt				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
879264790Sbapt					sbuf_printf(s, "(0x%08x)", f->desc->out);
880264790Sbapt				sbuf_printf(s, " -> ");
881264790Sbapt				f = f->parent;
882264790Sbapt			}
883264790Sbapt			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
884264790Sbapt		}
885264790Sbapt	} else
886264790Sbapt		sbuf_printf(s, " (mixer only)");
887319297Sdelphij	snd_mtxunlock(d->lock);
888264790Sbapt
889272655Sbapt	return 0;
890264790Sbapt}
891264790Sbapt
892272655Sbapt/************************************************************************/
893264790Sbapt
894264790Sbapt#ifdef SND_DYNSYSCTL
895264790Sbaptint
896264790Sbaptsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
897264790Sbapt{
898264790Sbapt	struct snddev_info *d;
899264790Sbapt    	struct snddev_channel *sce;
900264790Sbapt	struct pcm_channel *c;
901264790Sbapt	int err, newcnt, cnt, busy;
902264790Sbapt	int x;
903264790Sbapt
904264790Sbapt	d = oidp->oid_arg1;
905264790Sbapt
906264790Sbapt	x = pcm_inprog(d, 1);
907264790Sbapt	if (x != 1) {
908264790Sbapt		printf("x: %d\n", x);
909264790Sbapt		pcm_inprog(d, -1);
910264790Sbapt		return EINPROGRESS;
911264790Sbapt	}
912264790Sbapt
913264790Sbapt	busy = 0;
914264790Sbapt	cnt = 0;
915264790Sbapt	SLIST_FOREACH(sce, &d->channels, link) {
916264790Sbapt		c = sce->channel;
917264790Sbapt		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
918264790Sbapt			cnt++;
919264790Sbapt			if (c->flags & CHN_F_BUSY)
920264790Sbapt				busy++;
921264790Sbapt		}
922319297Sdelphij	}
923264790Sbapt
924272655Sbapt	newcnt = cnt;
925264790Sbapt
926264790Sbapt	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
927272655Sbapt
928264790Sbapt	if (err == 0 && req->newptr != NULL) {
929264790Sbapt
930264790Sbapt		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
931264790Sbapt			pcm_inprog(d, -1);
932264790Sbapt			return E2BIG;
933264790Sbapt		}
934264790Sbapt
935264790Sbapt		if (newcnt > cnt) {
936264790Sbapt			/* add new vchans - find a parent channel first */
937264790Sbapt			SLIST_FOREACH(sce, &d->channels, link) {
938264790Sbapt				c = sce->channel;
939264790Sbapt				/* not a candidate if not a play channel */
940264790Sbapt				if (c->direction != PCMDIR_PLAY)
941264790Sbapt					continue;
942264790Sbapt				/* not a candidate if a virtual channel */
943264790Sbapt				if (c->flags & CHN_F_VIRTUAL)
944319297Sdelphij					continue;
945264790Sbapt				/* not a candidate if it's in use */
946264790Sbapt				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
947264790Sbapt					continue;
948264790Sbapt				/*
949264790Sbapt				 * if we get here we're a nonvirtual play channel, and either
950264790Sbapt				 * 1) not busy
951264790Sbapt				 * 2) busy with children, not directly open
952264790Sbapt				 *
953264790Sbapt				 * thus we can add children
954264790Sbapt				 */
955264790Sbapt				goto addok;
956264790Sbapt			}
957264790Sbapt			pcm_inprog(d, -1);
958264790Sbapt			return EBUSY;
959264790Sbaptaddok:
960264790Sbapt			c->flags |= CHN_F_BUSY;
961264790Sbapt			while (err == 0 && newcnt > cnt) {
962264790Sbapt				err = vchan_create(c);
963264790Sbapt				if (err == 0)
964264790Sbapt					cnt++;
965264790Sbapt			}
966264790Sbapt			if (SLIST_EMPTY(&c->children))
967264790Sbapt				c->flags &= ~CHN_F_BUSY;
968264790Sbapt		} else if (newcnt < cnt) {
969264790Sbapt			if (busy > newcnt) {
970264790Sbapt				printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
971264790Sbapt				pcm_inprog(d, -1);
972264790Sbapt				return EBUSY;
973264790Sbapt			}
974264790Sbapt
975264790Sbapt			snd_mtxlock(d->lock);
976264790Sbapt			while (err == 0 && newcnt < cnt) {
977264790Sbapt				SLIST_FOREACH(sce, &d->channels, link) {
978264790Sbapt					c = sce->channel;
979264790Sbapt					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
980264790Sbapt						goto remok;
981264790Sbapt				}
982264790Sbapt				snd_mtxunlock(d->lock);
983264790Sbapt				pcm_inprog(d, -1);
984264790Sbapt				return EINVAL;
985264790Sbaptremok:
986264790Sbapt				err = vchan_destroy(c);
987264790Sbapt				if (err == 0)
988264790Sbapt					cnt--;
989264790Sbapt			}
990264790Sbapt			snd_mtxunlock(d->lock);
991264790Sbapt		}
992264790Sbapt	}
993264790Sbapt	pcm_inprog(d, -1);
994264790Sbapt	return err;
995264790Sbapt}
996264790Sbapt#endif
997264790Sbapt
998264790Sbapt/************************************************************************/
999264790Sbapt
1000264790Sbaptstatic moduledata_t sndpcm_mod = {
1001264790Sbapt	"snd_pcm",
1002264790Sbapt	NULL,
1003264790Sbapt	NULL
1004264790Sbapt};
1005264790SbaptDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
1006264790SbaptMODULE_VERSION(snd_pcm, PCM_MODVER);
1007264790Sbapt