Update all sound code to use the snd_*() locking abstraction and sndlock_t.
[dragonfly/vkernel-mp.git] / sys / dev / sound / pcm / sound.c
blob1c332ca1f2f89f2e158dbeef7a8bafbfd3f62dcd
1 /*-
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4 * All rights reserved.
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.
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.
27 * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.5 2007/06/04 09:06:05 ariff Exp $
28 * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.11 2007/06/16 20:07:22 dillon Exp $
31 #include <dev/sound/pcm/sound.h>
32 #include <dev/sound/pcm/vchan.h>
33 #include <dev/sound/pcm/dsp.h>
34 #include <sys/sysctl.h>
36 #include "feeder_if.h"
38 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.11 2007/06/16 20:07:22 dillon Exp $");
40 devclass_t pcm_devclass;
42 int pcm_veto_load = 1;
44 #ifdef USING_DEVFS
45 int snd_unit = 0;
46 TUNABLE_INT("hw.snd.unit", &snd_unit);
47 #endif
49 int snd_maxautovchans = 4;
50 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
52 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
54 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
56 struct sysctl_ctx_list *
57 snd_sysctl_tree(device_t dev)
59 struct snddev_info *d = device_get_softc(dev);
61 return &d->sysctl_tree;
64 struct sysctl_oid *
65 snd_sysctl_tree_top(device_t dev)
67 struct snddev_info *d = device_get_softc(dev);
69 return d->sysctl_tree_top;
72 void *
73 snd_mtxcreate(const char *desc, const char *type)
75 #ifdef USING_MUTEX
76 struct lock *m;
78 m = kmalloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
79 if (m == NULL)
80 return NULL;
81 lockinit(m, __DECONST(char *, type), 0, LK_CANRECURSE);
82 return m;
83 #else
84 return (void *)0xcafebabe;
85 #endif
88 void
89 snd_mtxfree(void *m)
91 #ifdef USING_MUTEX
92 struct lock *lk = m;
94 kfree(lk, M_DEVBUF);
95 #endif
98 void
99 snd_mtxassert(void *m)
101 #ifdef USING_MUTEX
102 #ifdef INVARIANTS
103 /* XXX */
104 #endif
105 #endif
108 void
109 snd_mtxlock(void *m)
111 #ifdef USING_MUTEX
112 struct lock *lk = m;
114 lockmgr(lk, LK_EXCLUSIVE | LK_RETRY);
115 #endif
118 void
119 snd_mtxunlock(void *m)
121 #ifdef USING_MUTEX
122 struct lock *lk = m;
124 lockmgr(lk, LK_RELEASE);
125 #endif
129 snd_mtxsleep(void *addr, sndlock_t lock, int flags, const char *wmesg, int timo)
131 int r;
133 crit_enter();
134 tsleep_interlock(addr);
135 snd_mtxunlock(lock);
136 r = tsleep(addr, flags, wmesg, timo);
137 snd_mtxlock(lock);
138 crit_exit();
139 return(r);
145 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
147 #ifdef USING_MUTEX
148 flags &= INTR_MPSAFE;
149 flags |= INTR_TYPE_AV;
150 #else
151 flags = INTR_TYPE_AV;
152 #endif
153 return bus_setup_intr(dev, res, flags, hand, param, cookiep, NULL);
156 #ifndef PCM_DEBUG_MTX
157 void
158 pcm_lock(struct snddev_info *d)
160 snd_mtxlock(d->lock);
163 void
164 pcm_unlock(struct snddev_info *d)
166 snd_mtxunlock(d->lock);
168 #endif
170 struct pcm_channel *
171 pcm_getfakechan(struct snddev_info *d)
173 return d->fakechan;
176 static int
177 pcm_setvchans(struct snddev_info *d, int newcnt)
179 struct snddev_channel *sce = NULL;
180 struct pcm_channel *c = NULL;
181 int err = 0, vcnt, dcnt, i;
183 pcm_inprog(d, 1);
185 if (!(d->flags & SD_F_AUTOVCHAN)) {
186 err = EINVAL;
187 goto setvchans_out;
190 vcnt = d->vchancount;
191 dcnt = d->playcount + d->reccount;
193 if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
194 err = E2BIG;
195 goto setvchans_out;
198 dcnt += vcnt;
200 if (newcnt > vcnt) {
201 /* add new vchans - find a parent channel first */
202 SLIST_FOREACH(sce, &d->channels, link) {
203 c = sce->channel;
204 CHN_LOCK(c);
205 if (c->direction == PCMDIR_PLAY &&
206 ((c->flags & CHN_F_HAS_VCHAN) ||
207 (vcnt == 0 &&
208 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
209 goto addok;
210 CHN_UNLOCK(c);
212 err = EBUSY;
213 goto setvchans_out;
214 addok:
215 c->flags |= CHN_F_BUSY;
216 while (err == 0 && newcnt > vcnt) {
217 if (dcnt > PCMMAXCHAN) {
218 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
219 break;
221 err = vchan_create(c);
222 if (err == 0) {
223 vcnt++;
224 dcnt++;
225 } else if (err == E2BIG && newcnt > vcnt)
226 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
228 if (vcnt == 0)
229 c->flags &= ~CHN_F_BUSY;
230 CHN_UNLOCK(c);
231 } else if (newcnt < vcnt) {
232 #define ORPHAN_CDEVT(cdevt) \
233 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
234 (cdevt)->si_drv2 == NULL))
235 while (err == 0 && newcnt < vcnt) {
236 i = 0;
237 SLIST_FOREACH(sce, &d->channels, link) {
238 c = sce->channel;
239 CHN_LOCK(c);
240 if (c->direction == PCMDIR_PLAY &&
241 (c->flags & CHN_F_VIRTUAL) &&
242 (i++ == newcnt)) {
243 if (!(c->flags & CHN_F_BUSY) &&
244 ORPHAN_CDEVT(sce->dsp_devt) &&
245 ORPHAN_CDEVT(sce->dspW_devt) &&
246 ORPHAN_CDEVT(sce->audio_devt) &&
247 ORPHAN_CDEVT(sce->dspr_devt))
248 goto remok;
250 * Either we're busy, or our cdev
251 * has been stolen by dsp_clone().
252 * Skip, and increase newcnt.
254 if (!(c->flags & CHN_F_BUSY))
255 device_printf(d->dev,
256 "%s: <%s> somebody steal my cdev!\n",
257 __func__, c->name);
258 newcnt++;
260 CHN_UNLOCK(c);
262 if (vcnt != newcnt)
263 err = EBUSY;
264 break;
265 remok:
266 CHN_UNLOCK(c);
267 err = vchan_destroy(c);
268 if (err == 0)
269 vcnt--;
270 else
271 device_printf(d->dev,
272 "%s: WARNING: vchan_destroy() failed!",
273 __func__);
277 setvchans_out:
278 pcm_inprog(d, -1);
279 return err;
282 /* return error status and a locked channel */
284 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
285 pid_t pid, int chnum)
287 struct pcm_channel *c;
288 struct snddev_channel *sce;
289 int err;
291 retry_chnalloc:
292 err = ENODEV;
293 /* scan for a free channel */
294 SLIST_FOREACH(sce, &d->channels, link) {
295 c = sce->channel;
296 CHN_LOCK(c);
297 if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
298 if (chnum < 0 || sce->chan_num == chnum) {
299 c->flags |= CHN_F_BUSY;
300 c->pid = pid;
301 *ch = c;
302 return 0;
305 if (sce->chan_num == chnum) {
306 if (c->direction != direction)
307 err = EOPNOTSUPP;
308 else if (c->flags & CHN_F_BUSY)
309 err = EBUSY;
310 else
311 err = EINVAL;
312 CHN_UNLOCK(c);
313 return err;
314 } else if (c->direction == direction && (c->flags & CHN_F_BUSY))
315 err = EBUSY;
316 CHN_UNLOCK(c);
319 /* no channel available */
320 if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
321 d->vchancount < snd_maxautovchans &&
322 d->devcount <= PCMMAXCHAN) {
323 err = pcm_setvchans(d, d->vchancount + 1);
324 if (err == 0) {
325 chnum = -2;
326 goto retry_chnalloc;
330 return err;
333 /* release a locked channel and unlock it */
335 pcm_chnrelease(struct pcm_channel *c)
337 CHN_LOCKASSERT(c);
338 c->flags &= ~CHN_F_BUSY;
339 c->pid = -1;
340 CHN_UNLOCK(c);
341 return 0;
345 pcm_chnref(struct pcm_channel *c, int ref)
347 int r;
349 CHN_LOCKASSERT(c);
350 c->refcount += ref;
351 r = c->refcount;
352 return r;
356 pcm_inprog(struct snddev_info *d, int delta)
358 int r;
360 if (delta == 0)
361 return d->inprog;
363 /* backtrace(); */
364 pcm_lock(d);
365 d->inprog += delta;
366 r = d->inprog;
367 pcm_unlock(d);
368 return r;
371 static void
372 pcm_setmaxautovchans(struct snddev_info *d, int num)
374 if (num > 0 && d->vchancount == 0)
375 pcm_setvchans(d, 1);
376 else if (num == 0 && d->vchancount > 0)
377 pcm_setvchans(d, 0);
380 #ifdef USING_DEVFS
381 static int
382 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
384 struct snddev_info *d;
385 int error, unit;
387 unit = snd_unit;
388 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
389 if (error == 0 && req->newptr != NULL) {
390 if (unit < 0 || (pcm_devclass != NULL &&
391 unit >= devclass_get_maxunit(pcm_devclass)))
392 return EINVAL;
393 d = devclass_get_softc(pcm_devclass, unit);
394 if (d == NULL || SLIST_EMPTY(&d->channels))
395 return EINVAL;
396 snd_unit = unit;
398 return (error);
400 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
401 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
402 #endif
404 static int
405 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
407 struct snddev_info *d;
408 int i, v, error;
410 v = snd_maxautovchans;
411 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
412 if (error == 0 && req->newptr != NULL) {
413 if (v < 0 || v > PCMMAXCHAN)
414 return E2BIG;
415 if (pcm_devclass != NULL && v != snd_maxautovchans) {
416 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
417 d = devclass_get_softc(pcm_devclass, i);
418 if (!d)
419 continue;
420 pcm_setmaxautovchans(d, v);
423 snd_maxautovchans = v;
425 return (error);
427 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
428 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
430 struct pcm_channel *
431 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
433 struct snddev_channel *sce;
434 struct pcm_channel *ch, *c;
435 char *dirs;
436 uint32_t flsearch = 0;
437 int direction, err, rpnum, *pnum;
439 switch(dir) {
440 case PCMDIR_PLAY:
441 dirs = "play";
442 direction = PCMDIR_PLAY;
443 pnum = &d->playcount;
444 break;
446 case PCMDIR_REC:
447 dirs = "record";
448 direction = PCMDIR_REC;
449 pnum = &d->reccount;
450 break;
452 case PCMDIR_VIRTUAL:
453 dirs = "virtual";
454 direction = PCMDIR_PLAY;
455 pnum = &d->vchancount;
456 flsearch = CHN_F_VIRTUAL;
457 break;
459 default:
460 return NULL;
463 ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
464 if (!ch)
465 return NULL;
467 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
468 if (!ch->methods) {
469 kfree(ch, M_DEVBUF);
471 return NULL;
474 snd_mtxlock(d->lock);
475 ch->num = 0;
476 rpnum = 0;
477 SLIST_FOREACH(sce, &d->channels, link) {
478 c = sce->channel;
479 if (direction != c->direction ||
480 (c->flags & CHN_F_VIRTUAL) != flsearch)
481 continue;
482 if (ch->num == c->num)
483 ch->num++;
484 else {
485 #if 0
486 device_printf(d->dev,
487 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
488 __func__, dirs, ch->num, c->num);
489 #endif
490 goto retry_num_search;
492 rpnum++;
494 goto retry_num_search_out;
495 retry_num_search:
496 rpnum = 0;
497 SLIST_FOREACH(sce, &d->channels, link) {
498 c = sce->channel;
499 if (direction != c->direction ||
500 (c->flags & CHN_F_VIRTUAL) != flsearch)
501 continue;
502 if (ch->num == c->num) {
503 ch->num++;
504 goto retry_num_search;
506 rpnum++;
508 retry_num_search_out:
509 if (*pnum != rpnum) {
510 device_printf(d->dev,
511 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
512 __func__, dirs, *pnum, rpnum);
513 *pnum = rpnum;
515 (*pnum)++;
516 snd_mtxunlock(d->lock);
518 ch->pid = -1;
519 ch->parentsnddev = d;
520 ch->parentchannel = parent;
521 ch->dev = d->dev;
522 ksnprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
524 err = chn_init(ch, devinfo, dir, direction);
525 if (err) {
526 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
527 kobj_delete(ch->methods, M_DEVBUF);
528 kfree(ch, M_DEVBUF);
529 snd_mtxlock(d->lock);
530 (*pnum)--;
531 snd_mtxunlock(d->lock);
533 return NULL;
536 return ch;
539 struct pcm_channel *
540 pcm_chn_iterate(struct snddev_info *d, void **cookie)
542 struct snddev_channel **last = (struct snddev_channel **)cookie;
544 if (*last == NULL)
545 *last = SLIST_FIRST(&d->channels);
546 else
547 *last = SLIST_NEXT(*last, link);
549 if (*last == NULL)
550 return NULL;
551 else
552 return (*last)->channel;
556 pcm_chn_destroy(struct pcm_channel *ch)
558 struct snddev_info *d;
559 int err;
561 d = ch->parentsnddev;
562 err = chn_kill(ch);
563 if (err) {
564 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
565 return err;
568 kobj_delete(ch->methods, M_DEVBUF);
569 kfree(ch, M_DEVBUF);
571 return 0;
575 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
577 struct snddev_channel *sce, *tmp, *after;
578 unsigned rdevcount;
579 int device = device_get_unit(d->dev);
580 size_t namelen;
583 * Note it's confusing nomenclature.
584 * dev_t
585 * device -> pcm_device
586 * unit -> pcm_channel
587 * channel -> snddev_channel
588 * device_t
589 * unit -> pcm_device
592 sce = kmalloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
593 if (!sce) {
594 return ENOMEM;
597 snd_mtxlock(d->lock);
598 sce->channel = ch;
599 sce->chan_num = 0;
600 rdevcount = 0;
601 after = NULL;
602 SLIST_FOREACH(tmp, &d->channels, link) {
603 if (sce->chan_num == tmp->chan_num)
604 sce->chan_num++;
605 else {
606 #if 0
607 device_printf(d->dev,
608 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
609 __func__, sce->chan_num, tmp->chan_num);
610 #endif
611 goto retry_chan_num_search;
613 after = tmp;
614 rdevcount++;
616 goto retry_chan_num_search_out;
617 retry_chan_num_search:
619 * Look for possible channel numbering collision. This may not
620 * be optimized, but it will ensure that no collision occured.
621 * Can be considered cheap since none of the locking/unlocking
622 * operations involved.
624 rdevcount = 0;
625 after = NULL;
626 SLIST_FOREACH(tmp, &d->channels, link) {
627 if (sce->chan_num == tmp->chan_num) {
628 sce->chan_num++;
629 goto retry_chan_num_search;
631 if (sce->chan_num > tmp->chan_num)
632 after = tmp;
633 rdevcount++;
635 retry_chan_num_search_out:
637 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
639 if (sce->chan_num > PCMMAXCHAN) {
640 snd_mtxunlock(d->lock);
641 device_printf(d->dev,
642 "%s: WARNING: sce->chan_num overflow! (%d)\n",
643 __func__, sce->chan_num);
644 kfree(sce, M_DEVBUF);
645 return E2BIG;
647 if (d->devcount != rdevcount) {
648 device_printf(d->dev,
649 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
650 __func__, d->devcount, rdevcount);
651 d->devcount = rdevcount;
653 d->devcount++;
654 if (after == NULL) {
655 SLIST_INSERT_HEAD(&d->channels, sce, link);
656 } else {
657 SLIST_INSERT_AFTER(after, sce, link);
659 #if 0
660 if (1) {
661 int cnum = 0;
662 SLIST_FOREACH(tmp, &d->channels, link) {
663 if (cnum != tmp->chan_num)
664 device_printf(d->dev,
665 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
666 __func__, cnum, tmp->chan_num);
667 cnum++;
670 #endif
672 namelen = strlen(ch->name);
673 if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
674 ksnprintf(ch->name + namelen,
675 CHN_NAMELEN - namelen, ":dsp%d.%d",
676 device, sce->chan_num);
678 snd_mtxunlock(d->lock);
681 * I will revisit these someday, and nuke it mercilessly..
683 dev_ops_add(&dsp_cdevsw,
684 PCMMKMINOR(-1, -1, 0),
685 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
686 sce->dsp_devt = make_dev(&dsp_cdevsw,
687 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
688 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
689 device, sce->chan_num);
690 reference_dev(sce->dsp_devt);
692 dev_ops_add(&dsp_cdevsw,
693 PCMMKMINOR(-1, -1, 0),
694 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num));
695 sce->dspW_devt = make_dev(&dsp_cdevsw,
696 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
697 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
698 device, sce->chan_num);
699 reference_dev(sce->dspW_devt);
702 dev_ops_add(&dsp_cdevsw,
703 PCMMKMINOR(-1, -1, 0),
704 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
705 sce->audio_devt = make_dev(&dsp_cdevsw,
706 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
707 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
708 device, sce->chan_num);
710 sce->audio_devt = sce->dsp_devt;
711 reference_dev(sce->audio_devt);
713 if (ch->direction == PCMDIR_REC) {
714 dev_ops_add(&dsp_cdevsw,
715 PCMMKMINOR(-1, -1, 0),
716 PCMMKMINOR(device, SND_DEV_DSPREC, sce->chan_num));
717 sce->dspr_devt = make_dev(&dsp_cdevsw,
718 PCMMKMINOR(device, SND_DEV_DSPREC,
719 sce->chan_num), UID_ROOT, GID_WHEEL,
720 0666, "dspr%d.%d", device, sce->chan_num);
721 reference_dev(sce->dspr_devt);
724 return 0;
728 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
730 struct snddev_channel *sce;
731 #if 0
732 int ourlock;
734 ourlock = 0;
735 if (!mtx_owned(d->lock)) {
736 snd_mtxlock(d->lock);
737 ourlock = 1;
739 #endif
741 SLIST_FOREACH(sce, &d->channels, link) {
742 if (sce->channel == ch)
743 goto gotit;
745 #if 0
746 if (ourlock)
747 snd_mtxunlock(d->lock);
748 #endif
749 return EINVAL;
750 gotit:
751 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
753 if (ch->flags & CHN_F_VIRTUAL)
754 d->vchancount--;
755 else if (ch->direction == PCMDIR_REC)
756 d->reccount--;
757 else
758 d->playcount--;
760 #if 0
761 if (ourlock)
762 snd_mtxunlock(d->lock);
763 #endif
764 kfree(sce, M_DEVBUF);
766 return 0;
770 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
772 struct snddev_info *d = device_get_softc(dev);
773 struct pcm_channel *ch;
774 int err;
776 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
777 if (!ch) {
778 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
779 return ENODEV;
782 err = pcm_chn_add(d, ch);
783 if (err) {
784 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
785 pcm_chn_destroy(ch);
786 return err;
789 return err;
792 static int
793 pcm_killchan(device_t dev)
795 struct snddev_info *d = device_get_softc(dev);
796 struct snddev_channel *sce;
797 struct pcm_channel *ch;
798 int error = 0;
800 sce = SLIST_FIRST(&d->channels);
801 ch = sce->channel;
803 error = pcm_chn_remove(d, sce->channel);
804 if (error)
805 return (error);
806 return (pcm_chn_destroy(ch));
810 pcm_setstatus(device_t dev, char *str)
812 struct snddev_info *d = device_get_softc(dev);
814 snd_mtxlock(d->lock);
815 strncpy(d->status, str, SND_STATUSLEN);
816 snd_mtxunlock(d->lock);
817 if (snd_maxautovchans > 0)
818 pcm_setvchans(d, 1);
819 return 0;
822 uint32_t
823 pcm_getflags(device_t dev)
825 struct snddev_info *d = device_get_softc(dev);
827 return d->flags;
830 void
831 pcm_setflags(device_t dev, uint32_t val)
833 struct snddev_info *d = device_get_softc(dev);
835 d->flags = val;
838 void *
839 pcm_getdevinfo(device_t dev)
841 struct snddev_info *d = device_get_softc(dev);
843 return d->devinfo;
846 unsigned int
847 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
849 struct snddev_info *d = device_get_softc(dev);
850 int sz, x;
852 sz = 0;
853 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
854 x = sz;
855 RANGE(sz, min, max);
856 if (x != sz)
857 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
858 x = min;
859 while (x < sz)
860 x <<= 1;
861 if (x > sz)
862 x >>= 1;
863 if (x != sz) {
864 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
865 sz = x;
867 } else {
868 sz = deflt;
871 d->bufsz = sz;
873 return sz;
877 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
879 struct snddev_info *d = device_get_softc(dev);
881 if (pcm_veto_load) {
882 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
884 return EINVAL;
887 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
889 #if 0
891 * d->flags should be cleared by the allocator of the softc.
892 * We cannot clear this field here because several devices set
893 * this flag before calling pcm_register().
895 d->flags = 0;
896 #endif
897 d->dev = dev;
898 d->devinfo = devinfo;
899 d->devcount = 0;
900 d->reccount = 0;
901 d->playcount = 0;
902 d->vchancount = 0;
903 d->inprog = 0;
905 SLIST_INIT(&d->channels);
907 if ((numplay == 0 || numrec == 0) && numplay != numrec)
908 d->flags |= SD_F_SIMPLEX;
910 d->fakechan = fkchan_setup(dev);
911 chn_init(d->fakechan, NULL, 0, 0);
913 #ifdef SND_DYNSYSCTL
914 sysctl_ctx_init(&d->sysctl_tree);
915 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
916 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
917 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
918 if (d->sysctl_tree_top == NULL) {
919 sysctl_ctx_free(&d->sysctl_tree);
920 goto no;
922 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
923 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
924 #endif
925 if (numplay > 0) {
926 d->flags |= SD_F_AUTOVCHAN;
927 vchan_initsys(dev);
930 sndstat_register(dev, d->status, sndstat_prepare_pcm);
931 return 0;
933 snd_mtxfree(d->lock);
934 return ENXIO;
938 pcm_unregister(device_t dev)
940 struct snddev_info *d = device_get_softc(dev);
941 struct snddev_channel *sce;
942 struct pcmchan_children *pce;
943 struct pcm_channel *ch;
945 if (sndstat_acquire() != 0) {
946 device_printf(dev, "unregister: sndstat busy\n");
947 return EBUSY;
950 snd_mtxlock(d->lock);
951 if (d->inprog) {
952 device_printf(dev, "unregister: operation in progress\n");
953 snd_mtxunlock(d->lock);
954 sndstat_release();
955 return EBUSY;
958 SLIST_FOREACH(sce, &d->channels, link) {
959 ch = sce->channel;
960 if (ch->refcount > 0) {
961 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
962 snd_mtxunlock(d->lock);
963 sndstat_release();
964 return EBUSY;
968 if (mixer_uninit(dev) == EBUSY) {
969 device_printf(dev, "unregister: mixer busy\n");
970 snd_mtxunlock(d->lock);
971 sndstat_release();
972 return EBUSY;
975 SLIST_FOREACH(sce, &d->channels, link) {
976 int unit = device_get_unit(d->dev);
978 if (sce->dsp_devt) {
979 release_dev(sce->dsp_devt);
980 dev_ops_remove(&dsp_cdevsw,
981 PCMMKMINOR(-1, -1, 0),
982 PCMMKMINOR(unit, SND_DEV_DSP, sce->chan_num));
983 sce->dsp_devt = NULL;
985 if (sce->dspW_devt) {
986 release_dev(sce->dspW_devt);
987 dev_ops_remove(&dsp_cdevsw,
988 PCMMKMINOR(-1, -1, 0),
989 PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
990 sce->dspW_devt = NULL;
992 if (sce->audio_devt) {
993 release_dev(sce->audio_devt);
995 dev_ops_remove(&dsp_cdevsw,
996 PCMMKMINOR(-1, -1, 0),
997 PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
999 sce->audio_devt = NULL;
1001 if (sce->dspr_devt) {
1002 release_dev(sce->dspr_devt);
1003 dev_ops_remove(&dsp_cdevsw,
1004 PCMMKMINOR(-1, -1, 0),
1005 PCMMKMINOR(unit, SND_DEV_DSPREC, sce->chan_num));
1006 sce->dspr_devt = NULL;
1008 d->devcount--;
1009 ch = sce->channel;
1010 if (ch == NULL)
1011 continue;
1012 pce = SLIST_FIRST(&ch->children);
1013 while (pce != NULL) {
1014 #if 0
1015 device_printf(d->dev, "<%s> removing <%s>\n",
1016 ch->name, (pce->channel != NULL) ?
1017 pce->channel->name : "unknown");
1018 #endif
1019 SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
1020 kfree(pce, M_DEVBUF);
1021 pce = SLIST_FIRST(&ch->children);
1025 #ifdef SND_DYNSYSCTL
1026 d->sysctl_tree_top = NULL;
1027 sysctl_ctx_free(&d->sysctl_tree);
1028 #endif
1030 #if 0
1031 SLIST_FOREACH(sce, &d->channels, link) {
1032 ch = sce->channel;
1033 if (ch == NULL)
1034 continue;
1035 if (!SLIST_EMPTY(&ch->children))
1036 device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
1037 __func__, ch->name);
1039 #endif
1040 while (!SLIST_EMPTY(&d->channels))
1041 pcm_killchan(dev);
1043 chn_kill(d->fakechan);
1044 fkchan_kill(d->fakechan);
1046 #if 0
1047 device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
1048 "reccount=%u, vchancount=%u\n",
1049 __func__, d->devcount, d->playcount, d->reccount,
1050 d->vchancount);
1051 #endif
1052 snd_mtxunlock(d->lock);
1053 snd_mtxfree(d->lock);
1054 sndstat_unregister(dev);
1055 sndstat_release();
1056 return 0;
1059 /************************************************************************/
1061 static int
1062 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1064 struct snddev_info *d;
1065 struct snddev_channel *sce;
1066 struct pcm_channel *c;
1067 struct pcm_feeder *f;
1068 int pc, rc, vc;
1070 if (verbose < 1)
1071 return 0;
1073 d = device_get_softc(dev);
1074 if (!d)
1075 return ENXIO;
1077 snd_mtxlock(d->lock);
1078 if (!SLIST_EMPTY(&d->channels)) {
1079 pc = rc = vc = 0;
1080 SLIST_FOREACH(sce, &d->channels, link) {
1081 c = sce->channel;
1082 if (c->direction == PCMDIR_PLAY) {
1083 if (c->flags & CHN_F_VIRTUAL)
1084 vc++;
1085 else
1086 pc++;
1087 } else
1088 rc++;
1090 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1091 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1092 #ifdef USING_DEVFS
1093 (device_get_unit(dev) == snd_unit)? " default" : ""
1094 #else
1096 #endif
1099 if (verbose <= 1) {
1100 snd_mtxunlock(d->lock);
1101 return 0;
1104 SLIST_FOREACH(sce, &d->channels, link) {
1105 c = sce->channel;
1107 KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1108 ("hosed pcm channel setup"));
1110 sbuf_printf(s, "\n\t");
1112 /* it would be better to indent child channels */
1113 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1114 sbuf_printf(s, "spd %d", c->speed);
1115 if (c->speed != sndbuf_getspd(c->bufhard))
1116 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1117 sbuf_printf(s, ", fmt 0x%08x", c->format);
1118 if (c->format != sndbuf_getfmt(c->bufhard))
1119 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1120 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1121 if (c->pid != -1)
1122 sbuf_printf(s, ", pid %d", c->pid);
1123 sbuf_printf(s, "\n\t");
1125 sbuf_printf(s, "interrupts %d, ", c->interrupts);
1126 if (c->direction == PCMDIR_REC)
1127 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1128 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1129 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1130 sndbuf_getblkcnt(c->bufhard),
1131 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1132 sndbuf_getblkcnt(c->bufsoft));
1133 else
1134 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1135 c->xruns, sndbuf_getready(c->bufsoft),
1136 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1137 sndbuf_getblkcnt(c->bufhard),
1138 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1139 sndbuf_getblkcnt(c->bufsoft));
1140 sbuf_printf(s, "\n\t");
1142 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1143 sbuf_printf(s, " -> ");
1144 f = c->feeder;
1145 while (f->source != NULL)
1146 f = f->source;
1147 while (f != NULL) {
1148 sbuf_printf(s, "%s", f->class->name);
1149 if (f->desc->type == FEEDER_FMT)
1150 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1151 if (f->desc->type == FEEDER_RATE)
1152 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1153 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1154 f->desc->type == FEEDER_VOLUME)
1155 sbuf_printf(s, "(0x%08x)", f->desc->out);
1156 sbuf_printf(s, " -> ");
1157 f = f->parent;
1159 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1161 } else
1162 sbuf_printf(s, " (mixer only)");
1163 snd_mtxunlock(d->lock);
1165 return 0;
1168 /************************************************************************/
1170 #ifdef SND_DYNSYSCTL
1172 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1174 struct snddev_info *d;
1175 int err, newcnt;
1177 d = oidp->oid_arg1;
1179 newcnt = d->vchancount;
1180 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1182 if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1183 err = pcm_setvchans(d, newcnt);
1185 return err;
1187 #endif
1189 /************************************************************************/
1191 static int
1192 sound_modevent(module_t mod, int type, void *data)
1194 #if 0
1195 return (midi_modevent(mod, type, data));
1196 #else
1197 return 0;
1198 #endif
1201 DEV_MODULE(sound, sound_modevent, NULL);
1202 MODULE_VERSION(sound, SOUND_MODVER);