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