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