sound.c revision 170884
1/*-
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3 * (C) 1997 Luigi Rizzo
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <dev/sound/pcm/sound.h>
29#include <dev/sound/pcm/ac97.h>
30#include <dev/sound/pcm/vchan.h>
31#include <dev/sound/pcm/dsp.h>
32#include <dev/sound/version.h>
33#include <sys/limits.h>
34#include <sys/sysctl.h>
35
36#include "feeder_if.h"
37
38SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 170884 2007-06-17 15:53:11Z ariff $");
39
40devclass_t pcm_devclass;
41
42int pcm_veto_load = 1;
43
44#ifdef USING_DEVFS
45int snd_unit = -1;
46TUNABLE_INT("hw.snd.default_unit", &snd_unit);
47#endif
48
49int snd_maxautovchans = 16;
50/* XXX: a tunable implies that we may need more than one sound channel before
51   the system can change a sysctl (/etc/sysctl.conf), do we really need
52   this? */
53TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
54
55SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
56
57/*
58 * XXX I've had enough with people not telling proper version/arch
59 *     while reporting problems, not after 387397913213th questions/requests.
60 */
61static const char snd_driver_version[] =
62    __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
63SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
64    0, "Driver version/arch");
65
66/**
67 * @brief Unit number allocator for syncgroup IDs
68 */
69struct unrhdr *pcmsg_unrhdr = NULL;
70
71static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
72
73void *
74snd_mtxcreate(const char *desc, const char *type)
75{
76#ifdef USING_MUTEX
77	struct mtx *m;
78
79	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
80	mtx_init(m, desc, type, MTX_DEF);
81	return m;
82#else
83	return (void *)0xcafebabe;
84#endif
85}
86
87void
88snd_mtxfree(void *m)
89{
90#ifdef USING_MUTEX
91	struct mtx *mtx = m;
92
93	/* mtx_assert(mtx, MA_OWNED); */
94	mtx_destroy(mtx);
95	free(mtx, M_DEVBUF);
96#endif
97}
98
99void
100snd_mtxassert(void *m)
101{
102#ifdef USING_MUTEX
103#ifdef INVARIANTS
104	struct mtx *mtx = m;
105
106	mtx_assert(mtx, MA_OWNED);
107#endif
108#endif
109}
110/*
111void
112snd_mtxlock(void *m)
113{
114#ifdef USING_MUTEX
115	struct mtx *mtx = m;
116
117	mtx_lock(mtx);
118#endif
119}
120
121void
122snd_mtxunlock(void *m)
123{
124#ifdef USING_MUTEX
125	struct mtx *mtx = m;
126
127	mtx_unlock(mtx);
128#endif
129}
130*/
131int
132snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
133{
134	struct snddev_info *d;
135#ifdef USING_MUTEX
136	flags &= INTR_MPSAFE;
137	flags |= INTR_TYPE_AV;
138#else
139	flags = INTR_TYPE_AV;
140#endif
141	d = device_get_softc(dev);
142	if (d != NULL && (flags & INTR_MPSAFE))
143		d->flags |= SD_F_MPSAFE;
144
145	return bus_setup_intr(dev, res, flags,
146#if __FreeBSD_version >= 700031
147			NULL,
148#endif
149			hand, param, cookiep);
150}
151
152#ifndef	PCM_DEBUG_MTX
153void
154pcm_lock(struct snddev_info *d)
155{
156	snd_mtxlock(d->lock);
157}
158
159void
160pcm_unlock(struct snddev_info *d)
161{
162	snd_mtxunlock(d->lock);
163}
164#endif
165
166struct pcm_channel *
167pcm_getfakechan(struct snddev_info *d)
168{
169	return d->fakechan;
170}
171
172static void
173pcm_clonereset(struct snddev_info *d)
174{
175	int cmax;
176
177	PCM_BUSYASSERT(d);
178
179	cmax = d->playcount + d->reccount - 1;
180	if (d->pvchancount > 0)
181		cmax += MAX(d->pvchancount, snd_maxautovchans) - 1;
182	if (d->rvchancount > 0)
183		cmax += MAX(d->rvchancount, snd_maxautovchans) - 1;
184	if (cmax > PCMMAXCLONE)
185		cmax = PCMMAXCLONE;
186	(void)snd_clone_gc(d->clones);
187	(void)snd_clone_setmaxunit(d->clones, cmax);
188}
189
190static int
191pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
192{
193	struct pcm_channel *c, *ch, *nch;
194	int err, vcnt;
195
196	PCM_BUSYASSERT(d);
197
198	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
199	    (direction == PCMDIR_REC && d->reccount < 1))
200		return (ENODEV);
201
202	if (!(d->flags & SD_F_AUTOVCHAN))
203		return (EINVAL);
204
205	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
206		return (E2BIG);
207
208	if (direction == PCMDIR_PLAY)
209		vcnt = d->pvchancount;
210	else if (direction == PCMDIR_REC)
211		vcnt = d->rvchancount;
212	else
213		return (EINVAL);
214
215	if (newcnt > vcnt) {
216		KASSERT(num == -1 ||
217		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
218		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
219		    num, newcnt, vcnt));
220		/* add new vchans - find a parent channel first */
221		ch = NULL;
222		CHN_FOREACH(c, d, channels.pcm) {
223			CHN_LOCK(c);
224			if (c->direction == direction &&
225			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
226			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
227				ch = c;
228			    	break;
229			}
230			CHN_UNLOCK(c);
231		}
232		if (ch == NULL)
233			return (EBUSY);
234		ch->flags |= CHN_F_BUSY;
235		err = 0;
236		while (err == 0 && newcnt > vcnt) {
237			err = vchan_create(ch, num);
238			if (err == 0)
239				vcnt++;
240			else if (err == E2BIG && newcnt > vcnt)
241				device_printf(d->dev,
242				    "%s: err=%d Maximum channel reached.\n",
243				    __func__, err);
244		}
245		if (vcnt == 0)
246			ch->flags &= ~CHN_F_BUSY;
247		CHN_UNLOCK(ch);
248		if (err != 0)
249			return (err);
250		else
251			pcm_clonereset(d);
252	} else if (newcnt < vcnt) {
253		KASSERT(num == -1,
254		    ("bogus vchan_destroy() request num=%d", num));
255		CHN_FOREACH(c, d, channels.pcm) {
256			CHN_LOCK(c);
257			if (c->direction != direction ||
258			    CHN_EMPTY(c, children) ||
259			    !(c->flags & CHN_F_HAS_VCHAN)) {
260				CHN_UNLOCK(c);
261				continue;
262			}
263			CHN_FOREACH_SAFE(ch, c, nch, children) {
264				CHN_LOCK(ch);
265				if (!(ch->flags & CHN_F_BUSY)) {
266					CHN_UNLOCK(ch);
267					CHN_UNLOCK(c);
268					err = vchan_destroy(ch);
269					CHN_LOCK(c);
270					if (err == 0)
271						vcnt--;
272				} else
273					CHN_UNLOCK(ch);
274				if (vcnt == newcnt)
275					break;
276			}
277			CHN_UNLOCK(c);
278			break;
279		}
280		pcm_clonereset(d);
281	}
282
283	return (0);
284}
285
286/* return error status and a locked channel */
287int
288pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
289    pid_t pid, int devunit)
290{
291	struct pcm_channel *c;
292	int err, vchancount;
293
294	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
295	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
296	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
297	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
298	    __func__, d, ch, direction, pid, devunit));
299	PCM_BUSYASSERT(d);
300
301	/* Double check again. */
302	if (devunit != -1) {
303		switch (snd_unit2d(devunit)) {
304		case SND_DEV_DSPHW_PLAY:
305		case SND_DEV_DSPHW_VPLAY:
306			if (direction != PCMDIR_PLAY)
307				return (EOPNOTSUPP);
308			break;
309		case SND_DEV_DSPHW_REC:
310		case SND_DEV_DSPHW_VREC:
311			if (direction != PCMDIR_REC)
312				return (EOPNOTSUPP);
313			break;
314		default:
315			if (!(direction == PCMDIR_PLAY ||
316			    direction == PCMDIR_REC))
317				return (EOPNOTSUPP);
318			break;
319		}
320	}
321
322retry_chnalloc:
323	err = EOPNOTSUPP;
324	/* scan for a free channel */
325	CHN_FOREACH(c, d, channels.pcm) {
326		CHN_LOCK(c);
327		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
328		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
329			c->flags |= CHN_F_BUSY;
330			c->pid = pid;
331			*ch = c;
332			return (0);
333		} else if (c->unit == devunit) {
334			if (c->direction != direction)
335				err = EOPNOTSUPP;
336			else if (c->flags & CHN_F_BUSY)
337				err = EBUSY;
338			else
339				err = EINVAL;
340			CHN_UNLOCK(c);
341			return (err);
342		} else if ((devunit == -1 || devunit == -2) &&
343		    c->direction == direction && (c->flags & CHN_F_BUSY))
344			err = EBUSY;
345		CHN_UNLOCK(c);
346	}
347
348	if (devunit == -2)
349		return (err);
350
351	/* no channel available */
352	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
353	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
354		if (direction == PCMDIR_PLAY)
355			vchancount = d->pvchancount;
356		else
357			vchancount = d->rvchancount;
358		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
359		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
360			return (err);
361		err = pcm_setvchans(d, direction, vchancount + 1,
362		    (devunit == -1) ? -1 : snd_unit2c(devunit));
363		if (err == 0) {
364			if (devunit == -1)
365				devunit = -2;
366			goto retry_chnalloc;
367		}
368	}
369
370	return (err);
371}
372
373/* release a locked channel and unlock it */
374int
375pcm_chnrelease(struct pcm_channel *c)
376{
377	PCM_BUSYASSERT(c->parentsnddev);
378	CHN_LOCKASSERT(c);
379
380	c->flags &= ~CHN_F_BUSY;
381	c->pid = -1;
382	CHN_UNLOCK(c);
383
384	return (0);
385}
386
387int
388pcm_chnref(struct pcm_channel *c, int ref)
389{
390	PCM_BUSYASSERT(c->parentsnddev);
391	CHN_LOCKASSERT(c);
392
393	c->refcount += ref;
394
395	return (c->refcount);
396}
397
398int
399pcm_inprog(struct snddev_info *d, int delta)
400{
401	snd_mtxassert(d->lock);
402
403	d->inprog += delta;
404
405	return (d->inprog);
406}
407
408static void
409pcm_setmaxautovchans(struct snddev_info *d, int num)
410{
411	PCM_BUSYASSERT(d);
412
413	if (num < 0)
414		return;
415
416	if (num >= 0 && d->pvchancount > num)
417		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
418	else if (num > 0 && d->pvchancount == 0)
419		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
420
421	if (num >= 0 && d->rvchancount > num)
422		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
423	else if (num > 0 && d->rvchancount == 0)
424		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
425
426	pcm_clonereset(d);
427}
428
429#ifdef USING_DEVFS
430static int
431sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
432{
433	struct snddev_info *d;
434	int error, unit;
435
436	unit = snd_unit;
437	error = sysctl_handle_int(oidp, &unit, 0, req);
438	if (error == 0 && req->newptr != NULL) {
439		d = devclass_get_softc(pcm_devclass, unit);
440		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
441			return EINVAL;
442		snd_unit = unit;
443	}
444	return (error);
445}
446/* XXX: do we need a way to let the user change the default unit? */
447SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
448            0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
449#endif
450
451static int
452sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
453{
454	struct snddev_info *d;
455	int i, v, error;
456
457	v = snd_maxautovchans;
458	error = sysctl_handle_int(oidp, &v, 0, req);
459	if (error == 0 && req->newptr != NULL) {
460		if (v < 0)
461			v = 0;
462		if (v > SND_MAXVCHANS)
463			v = SND_MAXVCHANS;
464		snd_maxautovchans = v;
465		for (i = 0; pcm_devclass != NULL &&
466		    i < devclass_get_maxunit(pcm_devclass); i++) {
467			d = devclass_get_softc(pcm_devclass, i);
468			if (!PCM_REGISTERED(d))
469				continue;
470			PCM_ACQUIRE_QUICK(d);
471			pcm_setmaxautovchans(d, v);
472			PCM_RELEASE_QUICK(d);
473		}
474	}
475	return (error);
476}
477SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
478            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
479
480struct pcm_channel *
481pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
482{
483	struct pcm_channel *ch;
484	int direction, err, rpnum, *pnum, max;
485	int udc, device, chan;
486	char *dirs, *devname, buf[CHN_NAMELEN];
487
488	PCM_BUSYASSERT(d);
489	snd_mtxassert(d->lock);
490	KASSERT(num >= -1, ("invalid num=%d", num));
491
492
493	switch (dir) {
494	case PCMDIR_PLAY:
495		dirs = "play";
496		direction = PCMDIR_PLAY;
497		pnum = &d->playcount;
498		device = SND_DEV_DSPHW_PLAY;
499		max = SND_MAXHWCHAN;
500		break;
501	case PCMDIR_PLAY_VIRTUAL:
502		dirs = "virtual";
503		direction = PCMDIR_PLAY;
504		pnum = &d->pvchancount;
505		device = SND_DEV_DSPHW_VPLAY;
506		max = SND_MAXVCHANS;
507		break;
508	case PCMDIR_REC:
509		dirs = "record";
510		direction = PCMDIR_REC;
511		pnum = &d->reccount;
512		device = SND_DEV_DSPHW_REC;
513		max = SND_MAXHWCHAN;
514		break;
515	case PCMDIR_REC_VIRTUAL:
516		dirs = "virtual";
517		direction = PCMDIR_REC;
518		pnum = &d->rvchancount;
519		device = SND_DEV_DSPHW_VREC;
520		max = SND_MAXVCHANS;
521		break;
522	default:
523		return (NULL);
524	}
525
526	chan = (num == -1) ? 0 : num;
527
528	if (*pnum >= max || chan >= max)
529		return (NULL);
530
531	rpnum = 0;
532
533	CHN_FOREACH(ch, d, channels.pcm) {
534		if (CHN_DEV(ch) != device)
535			continue;
536		if (chan == CHN_CHAN(ch)) {
537			if (num != -1) {
538				device_printf(d->dev,
539				    "channel num=%d allocated!\n", chan);
540				return (NULL);
541			}
542			chan++;
543			if (chan >= max) {
544				device_printf(d->dev,
545				    "chan=%d > %d\n", chan, max);
546				return (NULL);
547			}
548		}
549		rpnum++;
550	}
551
552	if (*pnum != rpnum) {
553		device_printf(d->dev,
554		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
555		    __func__, dirs, *pnum, rpnum);
556		return (NULL);
557	}
558
559	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
560	devname = dsp_unit2name(buf, sizeof(buf), udc);
561
562	if (devname == NULL) {
563		device_printf(d->dev,
564		    "Failed to query device name udc=0x%08x\n", udc);
565		return (NULL);
566	}
567
568	pcm_unlock(d);
569	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
570	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
571	ch->unit = udc;
572	ch->pid = -1;
573	ch->parentsnddev = d;
574	ch->parentchannel = parent;
575	ch->dev = d->dev;
576	ch->trigger = PCMTRIG_STOP;
577	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
578	    device_get_nameunit(ch->dev), dirs, devname);
579
580	err = chn_init(ch, devinfo, dir, direction);
581	pcm_lock(d);
582	if (err) {
583		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
584		    ch->name, err);
585		kobj_delete(ch->methods, M_DEVBUF);
586		free(ch, M_DEVBUF);
587		return (NULL);
588	}
589
590	return (ch);
591}
592
593int
594pcm_chn_destroy(struct pcm_channel *ch)
595{
596	struct snddev_info *d;
597	int err;
598
599	d = ch->parentsnddev;
600	PCM_BUSYASSERT(d);
601
602	err = chn_kill(ch);
603	if (err) {
604		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
605		    ch->name, err);
606		return (err);
607	}
608
609	kobj_delete(ch->methods, M_DEVBUF);
610	free(ch, M_DEVBUF);
611
612	return (0);
613}
614
615int
616pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
617{
618	struct pcm_channel *tmp, *after;
619	int num;
620
621	PCM_BUSYASSERT(d);
622	snd_mtxassert(d->lock);
623	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
624	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
625
626	after = NULL;
627	tmp = NULL;
628	num = 0;
629
630	/*
631	 * Look for possible device collision.
632	 */
633	CHN_FOREACH(tmp, d, channels.pcm) {
634		if (tmp->unit == ch->unit) {
635			device_printf(d->dev, "%s(): Device collision "
636			    "old=%p new=%p devunit=0x%08x\n",
637			    __func__, tmp, ch, ch->unit);
638			return (ENODEV);
639		}
640		if (CHN_DEV(tmp) < CHN_DEV(ch)) {
641			if (num == 0)
642				after = tmp;
643			continue;
644		} else if (CHN_DEV(tmp) > CHN_DEV(ch))
645			break;
646		num++;
647		if (CHN_CHAN(tmp) < CHN_CHAN(ch))
648			after = tmp;
649		else if (CHN_CHAN(tmp) > CHN_CHAN(ch))
650			break;
651	}
652
653	if (after != NULL) {
654		CHN_INSERT_AFTER(after, ch, channels.pcm);
655	} else {
656		CHN_INSERT_HEAD(d, ch, channels.pcm);
657	}
658
659	switch (CHN_DEV(ch)) {
660	case SND_DEV_DSPHW_PLAY:
661		d->playcount++;
662		break;
663	case SND_DEV_DSPHW_VPLAY:
664		d->pvchancount++;
665		break;
666	case SND_DEV_DSPHW_REC:
667		d->reccount++;
668		break;
669	case SND_DEV_DSPHW_VREC:
670		d->rvchancount++;
671		break;
672	default:
673		break;
674	}
675
676	d->devcount++;
677
678	return (0);
679}
680
681int
682pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
683{
684	struct pcm_channel *tmp;
685
686	PCM_BUSYASSERT(d);
687	snd_mtxassert(d->lock);
688
689	tmp = NULL;
690
691	CHN_FOREACH(tmp, d, channels.pcm) {
692		if (tmp == ch)
693			break;
694	}
695
696	if (tmp != ch)
697		return (EINVAL);
698
699	CHN_REMOVE(d, ch, channels.pcm);
700
701	switch (CHN_DEV(ch)) {
702	case SND_DEV_DSPHW_PLAY:
703		d->playcount--;
704		break;
705	case SND_DEV_DSPHW_VPLAY:
706		d->pvchancount--;
707		break;
708	case SND_DEV_DSPHW_REC:
709		d->reccount--;
710		break;
711	case SND_DEV_DSPHW_VREC:
712		d->rvchancount--;
713		break;
714	default:
715		break;
716	}
717
718	d->devcount--;
719
720	return (0);
721}
722
723int
724pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
725{
726	struct snddev_info *d = device_get_softc(dev);
727	struct pcm_channel *ch;
728	int err;
729
730	PCM_BUSYASSERT(d);
731
732	pcm_lock(d);
733	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
734	if (!ch) {
735		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
736		    cls->name, dir, devinfo);
737		pcm_unlock(d);
738		return (ENODEV);
739	}
740
741	err = pcm_chn_add(d, ch);
742	pcm_unlock(d);
743	if (err) {
744		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
745		    ch->name, err);
746		pcm_chn_destroy(ch);
747	}
748
749	return (err);
750}
751
752static int
753pcm_killchan(device_t dev)
754{
755	struct snddev_info *d = device_get_softc(dev);
756	struct pcm_channel *ch;
757	int error;
758
759	PCM_BUSYASSERT(d);
760
761	ch = CHN_FIRST(d, channels.pcm);
762
763	pcm_lock(d);
764	error = pcm_chn_remove(d, ch);
765	pcm_unlock(d);
766	if (error)
767		return (error);
768	return (pcm_chn_destroy(ch));
769}
770
771int
772pcm_setstatus(device_t dev, char *str)
773{
774	struct snddev_info *d = device_get_softc(dev);
775
776	PCM_BUSYASSERT(d);
777
778	if (d->playcount == 0 || d->reccount == 0)
779		d->flags |= SD_F_SIMPLEX;
780
781	if ((d->playcount > 0 || d->reccount > 0) &&
782	    !(d->flags & SD_F_AUTOVCHAN)) {
783		d->flags |= SD_F_AUTOVCHAN;
784		vchan_initsys(dev);
785	}
786
787	pcm_setmaxautovchans(d, snd_maxautovchans);
788
789	strlcpy(d->status, str, SND_STATUSLEN);
790
791	pcm_lock(d);
792
793	/* Last stage, enable cloning. */
794	if (d->clones != NULL)
795		(void)snd_clone_enable(d->clones);
796
797	/* Done, we're ready.. */
798	d->flags |= SD_F_REGISTERED;
799
800	PCM_RELEASE(d);
801
802	pcm_unlock(d);
803
804	if (snd_unit < 0)
805		snd_unit = device_get_unit(dev);
806
807	return (0);
808}
809
810uint32_t
811pcm_getflags(device_t dev)
812{
813	struct snddev_info *d = device_get_softc(dev);
814
815	return d->flags;
816}
817
818void
819pcm_setflags(device_t dev, uint32_t val)
820{
821	struct snddev_info *d = device_get_softc(dev);
822
823	d->flags = val;
824}
825
826void *
827pcm_getdevinfo(device_t dev)
828{
829	struct snddev_info *d = device_get_softc(dev);
830
831	return d->devinfo;
832}
833
834unsigned int
835pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
836{
837	struct snddev_info *d = device_get_softc(dev);
838	int sz, x;
839
840	sz = 0;
841	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
842		x = sz;
843		RANGE(sz, minbufsz, maxbufsz);
844		if (x != sz)
845			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
846		x = minbufsz;
847		while (x < sz)
848			x <<= 1;
849		if (x > sz)
850			x >>= 1;
851		if (x != sz) {
852			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
853			sz = x;
854		}
855	} else {
856		sz = deflt;
857	}
858
859	d->bufsz = sz;
860
861	return sz;
862}
863
864#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG)
865static int
866sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
867{
868	struct snddev_info *d;
869	uint32_t flags;
870	int err;
871
872	d = oidp->oid_arg1;
873	if (!PCM_REGISTERED(d) || d->clones == NULL)
874		return (ENODEV);
875
876	PCM_ACQUIRE_QUICK(d);
877
878	flags = snd_clone_getflags(d->clones);
879	err = sysctl_handle_int(oidp, &flags, 0, req);
880
881	if (err == 0 && req->newptr != NULL) {
882		if (flags & ~SND_CLONE_MASK)
883			err = EINVAL;
884		else
885			(void)snd_clone_setflags(d->clones, flags);
886	}
887
888	PCM_RELEASE_QUICK(d);
889
890	return (err);
891}
892
893static int
894sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
895{
896	struct snddev_info *d;
897	int err, deadline;
898
899	d = oidp->oid_arg1;
900	if (!PCM_REGISTERED(d) || d->clones == NULL)
901		return (ENODEV);
902
903	PCM_ACQUIRE_QUICK(d);
904
905	deadline = snd_clone_getdeadline(d->clones);
906	err = sysctl_handle_int(oidp, &deadline, 0, req);
907
908	if (err == 0 && req->newptr != NULL) {
909		if (deadline < 0)
910			err = EINVAL;
911		else
912			(void)snd_clone_setdeadline(d->clones, deadline);
913	}
914
915	PCM_RELEASE_QUICK(d);
916
917	return (err);
918}
919
920static int
921sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
922{
923	struct snddev_info *d;
924	int err, val;
925
926	d = oidp->oid_arg1;
927	if (!PCM_REGISTERED(d) || d->clones == NULL)
928		return (ENODEV);
929
930	val = 0;
931	err = sysctl_handle_int(oidp, &val, 0, req);
932
933	if (err == 0 && req->newptr != NULL && val != 0) {
934		PCM_ACQUIRE_QUICK(d);
935		val = snd_clone_gc(d->clones);
936		PCM_RELEASE_QUICK(d);
937		if (bootverbose != 0 || snd_verbose > 3)
938			device_printf(d->dev, "clone gc: pruned=%d\n", val);
939	}
940
941	return (err);
942}
943
944static int
945sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
946{
947	struct snddev_info *d;
948	int i, err, val;
949
950	val = 0;
951	err = sysctl_handle_int(oidp, &val, 0, req);
952
953	if (err == 0 && req->newptr != NULL && val != 0) {
954		for (i = 0; pcm_devclass != NULL &&
955		    i < devclass_get_maxunit(pcm_devclass); i++) {
956			d = devclass_get_softc(pcm_devclass, i);
957			if (!PCM_REGISTERED(d) || d->clones == NULL)
958				continue;
959			PCM_ACQUIRE_QUICK(d);
960			val = snd_clone_gc(d->clones);
961			PCM_RELEASE_QUICK(d);
962			if (bootverbose != 0 || snd_verbose > 3)
963				device_printf(d->dev, "clone gc: pruned=%d\n",
964				    val);
965		}
966	}
967
968	return (err);
969}
970SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
971    0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
972    "global clone garbage collector");
973#endif
974
975int
976pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
977{
978	struct snddev_info *d;
979
980	if (pcm_veto_load) {
981		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
982
983		return EINVAL;
984	}
985
986	if (device_get_unit(dev) > PCMMAXUNIT) {
987		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
988		    device_get_unit(dev), PCMMAXUNIT);
989		device_printf(dev,
990		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
991		return ENODEV;
992	}
993
994	d = device_get_softc(dev);
995	d->dev = dev;
996	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
997	cv_init(&d->cv, device_get_nameunit(dev));
998	PCM_ACQUIRE_QUICK(d);
999	dsp_cdevinfo_init(d);
1000#if 0
1001	/*
1002	 * d->flags should be cleared by the allocator of the softc.
1003	 * We cannot clear this field here because several devices set
1004	 * this flag before calling pcm_register().
1005	 */
1006	d->flags = 0;
1007#endif
1008	d->devinfo = devinfo;
1009	d->devcount = 0;
1010	d->reccount = 0;
1011	d->playcount = 0;
1012	d->pvchancount = 0;
1013	d->rvchancount = 0;
1014	d->pvchanrate = 0;
1015	d->pvchanformat = 0;
1016	d->rvchanrate = 0;
1017	d->rvchanformat = 0;
1018	d->inprog = 0;
1019
1020	/*
1021	 * Create clone manager, disabled by default. Cloning will be
1022	 * enabled during final stage of driver iniialization through
1023	 * pcm_setstatus().
1024	 */
1025	d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
1026	    SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
1027	    SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
1028	    SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
1029
1030	if (bootverbose != 0 || snd_verbose > 3) {
1031		device_printf(dev,
1032		    "clone manager: deadline=%dms flags=0x%08x\n",
1033		    snd_clone_getdeadline(d->clones),
1034		    snd_clone_getflags(d->clones));
1035	}
1036
1037	CHN_INIT(d, channels.pcm);
1038	CHN_INIT(d, channels.pcm.busy);
1039
1040	/* XXX This is incorrect, but lets play along for now. */
1041	if ((numplay == 0 || numrec == 0) && numplay != numrec)
1042		d->flags |= SD_F_SIMPLEX;
1043
1044	d->fakechan = fkchan_setup(dev);
1045	chn_init(d->fakechan, NULL, 0, 0);
1046
1047#ifdef SND_DYNSYSCTL
1048	sysctl_ctx_init(&d->play_sysctl_ctx);
1049	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
1050	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
1051	    CTLFLAG_RD, 0, "playback channels node");
1052	sysctl_ctx_init(&d->rec_sysctl_ctx);
1053	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
1054	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
1055	    CTLFLAG_RD, 0, "record channels node");
1056	/* XXX: an user should be able to set this with a control tool, the
1057	   sysadmin then needs min+max sysctls for this */
1058	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
1059	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
1060            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
1061#ifdef SND_DEBUG
1062	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1063	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1064	    "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
1065	    sysctl_dev_pcm_clone_flags, "IU",
1066	    "clone flags");
1067	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1068	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1069	    "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1070	    sysctl_dev_pcm_clone_deadline, "I",
1071	    "clone expiration deadline (ms)");
1072	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1073	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1074	    "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1075	    sysctl_dev_pcm_clone_gc, "I",
1076	    "clone garbage collector");
1077#endif
1078#endif
1079
1080	if (numplay > 0 || numrec > 0) {
1081		d->flags |= SD_F_AUTOVCHAN;
1082		vchan_initsys(dev);
1083	}
1084
1085	sndstat_register(dev, d->status, sndstat_prepare_pcm);
1086
1087	return 0;
1088}
1089
1090int
1091pcm_unregister(device_t dev)
1092{
1093	struct snddev_info *d;
1094	struct pcm_channel *ch;
1095	struct thread *td;
1096	int i;
1097
1098	td = curthread;
1099	d = device_get_softc(dev);
1100
1101	if (!PCM_ALIVE(d)) {
1102		device_printf(dev, "unregister: device not configured\n");
1103		return (0);
1104	}
1105
1106	if (sndstat_acquire(td) != 0) {
1107		device_printf(dev, "unregister: sndstat busy\n");
1108		return (EBUSY);
1109	}
1110
1111	pcm_lock(d);
1112	PCM_WAIT(d);
1113
1114	if (d->inprog != 0) {
1115		device_printf(dev, "unregister: operation in progress\n");
1116		pcm_unlock(d);
1117		sndstat_release(td);
1118		return (EBUSY);
1119	}
1120
1121	PCM_ACQUIRE(d);
1122	pcm_unlock(d);
1123
1124	CHN_FOREACH(ch, d, channels.pcm) {
1125		CHN_LOCK(ch);
1126		if (ch->refcount > 0) {
1127			device_printf(dev,
1128			    "unregister: channel %s busy (pid %d)\n",
1129			    ch->name, ch->pid);
1130			CHN_UNLOCK(ch);
1131			PCM_RELEASE_QUICK(d);
1132			sndstat_release(td);
1133			return (EBUSY);
1134		}
1135		CHN_UNLOCK(ch);
1136	}
1137
1138	if (d->clones != NULL) {
1139		if (snd_clone_busy(d->clones) != 0) {
1140			device_printf(dev, "unregister: clone busy\n");
1141			PCM_RELEASE_QUICK(d);
1142			sndstat_release(td);
1143			return (EBUSY);
1144		} else {
1145			pcm_lock(d);
1146			(void)snd_clone_disable(d->clones);
1147			pcm_unlock(d);
1148		}
1149	}
1150
1151	if (mixer_uninit(dev) == EBUSY) {
1152		device_printf(dev, "unregister: mixer busy\n");
1153		pcm_lock(d);
1154		if (d->clones != NULL)
1155			(void)snd_clone_enable(d->clones);
1156		PCM_RELEASE(d);
1157		pcm_unlock(d);
1158		sndstat_release(td);
1159		return (EBUSY);
1160	}
1161
1162	pcm_lock(d);
1163	d->flags |= SD_F_DYING;
1164	d->flags &= ~SD_F_REGISTERED;
1165	pcm_unlock(d);
1166
1167	/*
1168	 * No lock being held, so this thing can be flushed without
1169	 * stucking into devdrn oblivion.
1170	 */
1171	if (d->clones != NULL) {
1172		snd_clone_destroy(d->clones);
1173		d->clones = NULL;
1174	}
1175
1176#ifdef SND_DYNSYSCTL
1177	if (d->play_sysctl_tree != NULL) {
1178		sysctl_ctx_free(&d->play_sysctl_ctx);
1179		d->play_sysctl_tree = NULL;
1180	}
1181	if (d->rec_sysctl_tree != NULL) {
1182		sysctl_ctx_free(&d->rec_sysctl_ctx);
1183		d->rec_sysctl_tree = NULL;
1184	}
1185#endif
1186
1187	while (!CHN_EMPTY(d, channels.pcm))
1188		pcm_killchan(dev);
1189
1190	chn_kill(d->fakechan);
1191	fkchan_kill(d->fakechan);
1192
1193	dsp_cdevinfo_flush(d);
1194
1195	pcm_lock(d);
1196	PCM_RELEASE(d);
1197	cv_destroy(&d->cv);
1198	pcm_unlock(d);
1199	snd_mtxfree(d->lock);
1200	sndstat_unregister(dev);
1201	sndstat_release(td);
1202
1203	if (snd_unit == device_get_unit(dev)) {
1204		/*
1205		 * Reassign default unit to the next available dev, but
1206		 * first, reset snd_unit to something ridiculous.
1207		 */
1208		snd_unit = -1;
1209		for (i = 0; pcm_devclass != NULL &&
1210		    i < devclass_get_maxunit(pcm_devclass); i++) {
1211			if (device_get_unit(dev) == i)
1212				continue;
1213			d = devclass_get_softc(pcm_devclass, i);
1214			if (PCM_REGISTERED(d)) {
1215				snd_unit = i;
1216				break;
1217			}
1218		}
1219	}
1220
1221	return (0);
1222}
1223
1224/************************************************************************/
1225
1226static int
1227sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1228{
1229	struct snddev_info *d;
1230	struct pcm_channel *c;
1231	struct pcm_feeder *f;
1232
1233	if (verbose < 1)
1234		return 0;
1235
1236	d = device_get_softc(dev);
1237	if (!d)
1238		return ENXIO;
1239
1240	PCM_BUSYASSERT(d);
1241
1242	if (CHN_EMPTY(d, channels.pcm)) {
1243		sbuf_printf(s, " (mixer only)");
1244		return 0;
1245	}
1246
1247	sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
1248			d->playcount, d->pvchancount,
1249			d->reccount, d->rvchancount,
1250			(d->flags & SD_F_SIMPLEX)? "" : " duplex",
1251#ifdef USING_DEVFS
1252			(device_get_unit(dev) == snd_unit)? " default" : ""
1253#else
1254			""
1255#endif
1256			);
1257
1258	if (verbose <= 1)
1259		return 0;
1260
1261	CHN_FOREACH(c, d, channels.pcm) {
1262
1263		KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1264			("hosed pcm channel setup"));
1265
1266		sbuf_printf(s, "\n\t");
1267
1268		/* it would be better to indent child channels */
1269		sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1270		sbuf_printf(s, "spd %d", c->speed);
1271		if (c->speed != sndbuf_getspd(c->bufhard))
1272			sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1273		sbuf_printf(s, ", fmt 0x%08x", c->format);
1274		if (c->format != sndbuf_getfmt(c->bufhard))
1275			sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1276		sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1277		if (c->pid != -1)
1278			sbuf_printf(s, ", pid %d", c->pid);
1279		sbuf_printf(s, "\n\t");
1280
1281		sbuf_printf(s, "interrupts %d, ", c->interrupts);
1282		if (c->direction == PCMDIR_REC)
1283			sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1284				c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1285				sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1286				sndbuf_getblkcnt(c->bufhard),
1287				sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1288				sndbuf_getblkcnt(c->bufsoft));
1289		else
1290			sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1291				c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
1292				sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1293				sndbuf_getblkcnt(c->bufhard),
1294				sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1295				sndbuf_getblkcnt(c->bufsoft));
1296		sbuf_printf(s, "\n\t");
1297
1298		sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1299		sbuf_printf(s, " -> ");
1300		f = c->feeder;
1301		while (f->source != NULL)
1302			f = f->source;
1303		while (f != NULL) {
1304			sbuf_printf(s, "%s", f->class->name);
1305			if (f->desc->type == FEEDER_FMT)
1306				sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1307			if (f->desc->type == FEEDER_RATE)
1308				sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1309			if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1310					f->desc->type == FEEDER_VOLUME)
1311				sbuf_printf(s, "(0x%08x)", f->desc->out);
1312			sbuf_printf(s, " -> ");
1313			f = f->parent;
1314		}
1315		sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1316	}
1317
1318	return 0;
1319}
1320
1321/************************************************************************/
1322
1323#ifdef SND_DYNSYSCTL
1324int
1325sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1326{
1327	struct snddev_info *d;
1328	int direction, vchancount;
1329	int err, cnt;
1330
1331	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
1332	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
1333		return (EINVAL);
1334
1335	pcm_lock(d);
1336	PCM_WAIT(d);
1337
1338	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
1339	case VCHAN_PLAY:
1340		direction = PCMDIR_PLAY;
1341		vchancount = d->pvchancount;
1342		cnt = d->playcount;
1343		break;
1344	case VCHAN_REC:
1345		direction = PCMDIR_REC;
1346		vchancount = d->rvchancount;
1347		cnt = d->reccount;
1348		break;
1349	default:
1350		pcm_unlock(d);
1351		return (EINVAL);
1352		break;
1353	}
1354
1355	if (cnt < 1) {
1356		pcm_unlock(d);
1357		return (ENODEV);
1358	}
1359
1360	PCM_ACQUIRE(d);
1361	pcm_unlock(d);
1362
1363	cnt = vchancount;
1364	err = sysctl_handle_int(oidp, &cnt, 0, req);
1365
1366	if (err == 0 && req->newptr != NULL && vchancount != cnt) {
1367		if (cnt < 0)
1368			cnt = 0;
1369		if (cnt > SND_MAXVCHANS)
1370			cnt = SND_MAXVCHANS;
1371		err = pcm_setvchans(d, direction, cnt, -1);
1372	}
1373
1374	PCM_RELEASE_QUICK(d);
1375
1376	return err;
1377}
1378#endif
1379
1380/************************************************************************/
1381
1382/**
1383 * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1384 *
1385 * @param si	Pointer to oss_sysinfo struct where information about the
1386 * 		sound subsystem will be written/copied.
1387 *
1388 * This routine returns information about the sound system, such as the
1389 * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1390 * Also includes a bitmask showing which of the above types of devices
1391 * are open (busy).
1392 *
1393 * @note
1394 * Calling threads must not hold any snddev_info or pcm_channel locks.
1395 *
1396 * @author	Ryan Beasley <ryanb@FreeBSD.org>
1397 */
1398void
1399sound_oss_sysinfo(oss_sysinfo *si)
1400{
1401	static char si_product[] = "FreeBSD native OSS ABI";
1402	static char si_version[] = __XSTRING(__FreeBSD_version);
1403	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1404						   Must pester a C guru. */
1405
1406	struct snddev_info *d;
1407	struct pcm_channel *c;
1408	int i, j, ncards;
1409
1410	ncards = 0;
1411
1412	strlcpy(si->product, si_product, sizeof(si->product));
1413	strlcpy(si->version, si_version, sizeof(si->version));
1414	si->versionnum = SOUND_VERSION;
1415
1416	/*
1417	 * Iterate over PCM devices and their channels, gathering up data
1418	 * for the numaudios, ncards, and openedaudio fields.
1419	 */
1420	si->numaudios = 0;
1421	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1422
1423	j = 0;
1424
1425	for (i = 0; pcm_devclass != NULL &&
1426	    i < devclass_get_maxunit(pcm_devclass); i++) {
1427		d = devclass_get_softc(pcm_devclass, i);
1428		if (!PCM_REGISTERED(d))
1429			continue;
1430
1431		/* XXX Need Giant magic entry ??? */
1432
1433		/* See note in function's docblock */
1434		mtx_assert(d->lock, MA_NOTOWNED);
1435		pcm_lock(d);
1436
1437		si->numaudios += d->devcount;
1438		++ncards;
1439
1440		CHN_FOREACH(c, d, channels.pcm) {
1441			mtx_assert(c->lock, MA_NOTOWNED);
1442			CHN_LOCK(c);
1443			if (c->flags & CHN_F_BUSY)
1444				si->openedaudio[j / intnbits] |=
1445				    (1 << (j % intnbits));
1446			CHN_UNLOCK(c);
1447			j++;
1448		}
1449
1450		pcm_unlock(d);
1451	}
1452
1453	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1454	/**
1455	 * @todo	Collect num{midis,timers}.
1456	 *
1457	 * Need access to sound/midi/midi.c::midistat_lock in order
1458	 * to safely touch midi_devices and get a head count of, well,
1459	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1460	 * midi.c), but midi_devices is a regular global; should the mutex
1461	 * be publicized, or is there another way to get this information?
1462	 *
1463	 * NB:	MIDI/sequencer stuff is currently on hold.
1464	 */
1465	si->nummidis = 0;
1466	si->numtimers = 0;
1467	si->nummixers = mixer_count;
1468	si->numcards = ncards;
1469		/* OSSv4 docs:	Intended only for test apps; API doesn't
1470		   really have much of a concept of cards.  Shouldn't be
1471		   used by applications. */
1472
1473	/**
1474	 * @todo	Fill in "busy devices" fields.
1475	 *
1476	 *  si->openedmidi = " MIDI devices
1477	 */
1478	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1479
1480	/*
1481	 * Si->filler is a reserved array, but according to docs each
1482	 * element should be set to -1.
1483	 */
1484	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1485		si->filler[i] = -1;
1486}
1487
1488/************************************************************************/
1489
1490static int
1491sound_modevent(module_t mod, int type, void *data)
1492{
1493	int ret;
1494#if 0
1495	return (midi_modevent(mod, type, data));
1496#else
1497	ret = 0;
1498
1499	switch(type) {
1500		case MOD_LOAD:
1501			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1502			break;
1503		case MOD_UNLOAD:
1504		case MOD_SHUTDOWN:
1505			ret = sndstat_acquire(curthread);
1506			if (ret != 0)
1507				break;
1508			if (pcmsg_unrhdr != NULL) {
1509				delete_unrhdr(pcmsg_unrhdr);
1510				pcmsg_unrhdr = NULL;
1511			}
1512			break;
1513		default:
1514			ret = EOPNOTSUPP;
1515	}
1516
1517	return ret;
1518#endif
1519}
1520
1521DEV_MODULE(sound, sound_modevent, NULL);
1522MODULE_VERSION(sound, SOUND_MODVER);
1523