1 /*-
2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <dev/sound/pcm/sound.h>
29 #include <dev/sound/pcm/vchan.h>
30 #include <dev/sound/pcm/dsp.h>
31 #include <sys/sysctl.h>
32
33 #include "feeder_if.h"
34
35 SND_DECLARE_FILE("$FreeBSD: releng/6.4/sys/dev/sound/pcm/sound.c 170279 2007-06-04 09:06:05Z ariff $");
36
37 devclass_t pcm_devclass;
38
39 int pcm_veto_load = 1;
40
41 #ifdef USING_DEVFS
42 int snd_unit = 0;
43 TUNABLE_INT("hw.snd.unit", &snd_unit);
44 #endif
45
46 int snd_maxautovchans = 4;
47 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
48
49 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
50
51 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
52
53 struct sysctl_ctx_list *
54 snd_sysctl_tree(device_t dev)
55 {
56 struct snddev_info *d = device_get_softc(dev);
57
58 return &d->sysctl_tree;
59 }
60
61 struct sysctl_oid *
62 snd_sysctl_tree_top(device_t dev)
63 {
64 struct snddev_info *d = device_get_softc(dev);
65
66 return d->sysctl_tree_top;
67 }
68
69 void *
70 snd_mtxcreate(const char *desc, const char *type)
71 {
72 #ifdef USING_MUTEX
73 struct mtx *m;
74
75 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
76 if (m == NULL)
77 return NULL;
78 mtx_init(m, desc, type, MTX_DEF);
79 return m;
80 #else
81 return (void *)0xcafebabe;
82 #endif
83 }
84
85 void
86 snd_mtxfree(void *m)
87 {
88 #ifdef USING_MUTEX
89 struct mtx *mtx = m;
90
91 /* mtx_assert(mtx, MA_OWNED); */
92 mtx_destroy(mtx);
93 free(mtx, M_DEVBUF);
94 #endif
95 }
96
97 void
98 snd_mtxassert(void *m)
99 {
100 #ifdef USING_MUTEX
101 #ifdef INVARIANTS
102 struct mtx *mtx = m;
103
104 mtx_assert(mtx, MA_OWNED);
105 #endif
106 #endif
107 }
108 /*
109 void
110 snd_mtxlock(void *m)
111 {
112 #ifdef USING_MUTEX
113 struct mtx *mtx = m;
114
115 mtx_lock(mtx);
116 #endif
117 }
118
119 void
120 snd_mtxunlock(void *m)
121 {
122 #ifdef USING_MUTEX
123 struct mtx *mtx = m;
124
125 mtx_unlock(mtx);
126 #endif
127 }
128 */
129 int
130 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
131 {
132 #ifdef USING_MUTEX
133 flags &= INTR_MPSAFE;
134 flags |= INTR_TYPE_AV;
135 #else
136 flags = INTR_TYPE_AV;
137 #endif
138 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
139 }
140
141 #ifndef PCM_DEBUG_MTX
142 void
143 pcm_lock(struct snddev_info *d)
144 {
145 snd_mtxlock(d->lock);
146 }
147
148 void
149 pcm_unlock(struct snddev_info *d)
150 {
151 snd_mtxunlock(d->lock);
152 }
153 #endif
154
155 struct pcm_channel *
156 pcm_getfakechan(struct snddev_info *d)
157 {
158 return d->fakechan;
159 }
160
161 static int
162 pcm_setvchans(struct snddev_info *d, int newcnt)
163 {
164 struct snddev_channel *sce = NULL;
165 struct pcm_channel *c = NULL;
166 int err = 0, vcnt, dcnt, i;
167
168 pcm_inprog(d, 1);
169
170 if (!(d->flags & SD_F_AUTOVCHAN)) {
171 err = EINVAL;
172 goto setvchans_out;
173 }
174
175 vcnt = d->vchancount;
176 dcnt = d->playcount + d->reccount;
177
178 if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
179 err = E2BIG;
180 goto setvchans_out;
181 }
182
183 dcnt += vcnt;
184
185 if (newcnt > vcnt) {
186 /* add new vchans - find a parent channel first */
187 SLIST_FOREACH(sce, &d->channels, link) {
188 c = sce->channel;
189 CHN_LOCK(c);
190 if (c->direction == PCMDIR_PLAY &&
191 ((c->flags & CHN_F_HAS_VCHAN) ||
192 (vcnt == 0 &&
193 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
194 goto addok;
195 CHN_UNLOCK(c);
196 }
197 err = EBUSY;
198 goto setvchans_out;
199 addok:
200 c->flags |= CHN_F_BUSY;
201 while (err == 0 && newcnt > vcnt) {
202 if (dcnt > PCMMAXCHAN) {
203 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
204 break;
205 }
206 err = vchan_create(c);
207 if (err == 0) {
208 vcnt++;
209 dcnt++;
210 } else if (err == E2BIG && newcnt > vcnt)
211 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
212 }
213 if (vcnt == 0)
214 c->flags &= ~CHN_F_BUSY;
215 CHN_UNLOCK(c);
216 } else if (newcnt < vcnt) {
217 #define ORPHAN_CDEVT(cdevt) \
218 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
219 (cdevt)->si_drv2 == NULL))
220 while (err == 0 && newcnt < vcnt) {
221 i = 0;
222 SLIST_FOREACH(sce, &d->channels, link) {
223 c = sce->channel;
224 CHN_LOCK(c);
225 if (c->direction == PCMDIR_PLAY &&
226 (c->flags & CHN_F_VIRTUAL) &&
227 (i++ == newcnt)) {
228 if (!(c->flags & CHN_F_BUSY) &&
229 ORPHAN_CDEVT(sce->dsp_devt) &&
230 ORPHAN_CDEVT(sce->dspW_devt) &&
231 ORPHAN_CDEVT(sce->audio_devt) &&
232 ORPHAN_CDEVT(sce->dspr_devt))
233 goto remok;
234 /*
235 * Either we're busy, or our cdev
236 * has been stolen by dsp_clone().
237 * Skip, and increase newcnt.
238 */
239 if (!(c->flags & CHN_F_BUSY))
240 device_printf(d->dev,
241 "%s: <%s> somebody steal my cdev!\n",
242 __func__, c->name);
243 newcnt++;
244 }
245 CHN_UNLOCK(c);
246 }
247 if (vcnt != newcnt)
248 err = EBUSY;
249 break;
250 remok:
251 CHN_UNLOCK(c);
252 err = vchan_destroy(c);
253 if (err == 0)
254 vcnt--;
255 else
256 device_printf(d->dev,
257 "%s: WARNING: vchan_destroy() failed!",
258 __func__);
259 }
260 }
261
262 setvchans_out:
263 pcm_inprog(d, -1);
264 return err;
265 }
266
267 /* return error status and a locked channel */
268 int
269 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
270 pid_t pid, int chnum)
271 {
272 struct pcm_channel *c;
273 struct snddev_channel *sce;
274 int err;
275
276 retry_chnalloc:
277 err = ENODEV;
278 /* scan for a free channel */
279 SLIST_FOREACH(sce, &d->channels, link) {
280 c = sce->channel;
281 CHN_LOCK(c);
282 if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
283 if (chnum < 0 || sce->chan_num == chnum) {
284 c->flags |= CHN_F_BUSY;
285 c->pid = pid;
286 *ch = c;
287 return 0;
288 }
289 }
290 if (sce->chan_num == chnum) {
291 if (c->direction != direction)
292 err = EOPNOTSUPP;
293 else if (c->flags & CHN_F_BUSY)
294 err = EBUSY;
295 else
296 err = EINVAL;
297 CHN_UNLOCK(c);
298 return err;
299 } else if (c->direction == direction && (c->flags & CHN_F_BUSY))
300 err = EBUSY;
301 CHN_UNLOCK(c);
302 }
303
304 /* no channel available */
305 if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
306 d->vchancount < snd_maxautovchans &&
307 d->devcount <= PCMMAXCHAN) {
308 err = pcm_setvchans(d, d->vchancount + 1);
309 if (err == 0) {
310 chnum = -2;
311 goto retry_chnalloc;
312 }
313 }
314
315 return err;
316 }
317
318 /* release a locked channel and unlock it */
319 int
320 pcm_chnrelease(struct pcm_channel *c)
321 {
322 CHN_LOCKASSERT(c);
323 c->flags &= ~CHN_F_BUSY;
324 c->pid = -1;
325 CHN_UNLOCK(c);
326 return 0;
327 }
328
329 int
330 pcm_chnref(struct pcm_channel *c, int ref)
331 {
332 int r;
333
334 CHN_LOCKASSERT(c);
335 c->refcount += ref;
336 r = c->refcount;
337 return r;
338 }
339
340 int
341 pcm_inprog(struct snddev_info *d, int delta)
342 {
343 int r;
344
345 if (delta == 0)
346 return d->inprog;
347
348 /* backtrace(); */
349 pcm_lock(d);
350 d->inprog += delta;
351 r = d->inprog;
352 pcm_unlock(d);
353 return r;
354 }
355
356 static void
357 pcm_setmaxautovchans(struct snddev_info *d, int num)
358 {
359 if (num > 0 && d->vchancount == 0)
360 pcm_setvchans(d, 1);
361 else if (num == 0 && d->vchancount > 0)
362 pcm_setvchans(d, 0);
363 }
364
365 #ifdef USING_DEVFS
366 static int
367 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
368 {
369 struct snddev_info *d;
370 int error, unit;
371
372 unit = snd_unit;
373 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
374 if (error == 0 && req->newptr != NULL) {
375 if (unit < 0 || (pcm_devclass != NULL &&
376 unit >= devclass_get_maxunit(pcm_devclass)))
377 return EINVAL;
378 d = devclass_get_softc(pcm_devclass, unit);
379 if (d == NULL || SLIST_EMPTY(&d->channels))
380 return EINVAL;
381 snd_unit = unit;
382 }
383 return (error);
384 }
385 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
386 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
387 #endif
388
389 static int
390 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
391 {
392 struct snddev_info *d;
393 int i, v, error;
394
395 v = snd_maxautovchans;
396 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
397 if (error == 0 && req->newptr != NULL) {
398 if (v < 0 || v > PCMMAXCHAN)
399 return E2BIG;
400 if (pcm_devclass != NULL && v != snd_maxautovchans) {
401 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
402 d = devclass_get_softc(pcm_devclass, i);
403 if (!d)
404 continue;
405 pcm_setmaxautovchans(d, v);
406 }
407 }
408 snd_maxautovchans = v;
409 }
410 return (error);
411 }
412 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
413 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
414
415 struct pcm_channel *
416 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
417 {
418 struct snddev_channel *sce;
419 struct pcm_channel *ch, *c;
420 char *dirs;
421 uint32_t flsearch = 0;
422 int direction, err, rpnum, *pnum;
423
424 switch(dir) {
425 case PCMDIR_PLAY:
426 dirs = "play";
427 direction = PCMDIR_PLAY;
428 pnum = &d->playcount;
429 break;
430
431 case PCMDIR_REC:
432 dirs = "record";
433 direction = PCMDIR_REC;
434 pnum = &d->reccount;
435 break;
436
437 case PCMDIR_VIRTUAL:
438 dirs = "virtual";
439 direction = PCMDIR_PLAY;
440 pnum = &d->vchancount;
441 flsearch = CHN_F_VIRTUAL;
442 break;
443
444 default:
445 return NULL;
446 }
447
448 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
449 if (!ch)
450 return NULL;
451
452 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
453 if (!ch->methods) {
454 free(ch, M_DEVBUF);
455
456 return NULL;
457 }
458
459 snd_mtxlock(d->lock);
460 ch->num = 0;
461 rpnum = 0;
462 SLIST_FOREACH(sce, &d->channels, link) {
463 c = sce->channel;
464 if (direction != c->direction ||
465 (c->flags & CHN_F_VIRTUAL) != flsearch)
466 continue;
467 if (ch->num == c->num)
468 ch->num++;
469 else {
470 #if 0
471 device_printf(d->dev,
472 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
473 __func__, dirs, ch->num, c->num);
474 #endif
475 goto retry_num_search;
476 }
477 rpnum++;
478 }
479 goto retry_num_search_out;
480 retry_num_search:
481 rpnum = 0;
482 SLIST_FOREACH(sce, &d->channels, link) {
483 c = sce->channel;
484 if (direction != c->direction ||
485 (c->flags & CHN_F_VIRTUAL) != flsearch)
486 continue;
487 if (ch->num == c->num) {
488 ch->num++;
489 goto retry_num_search;
490 }
491 rpnum++;
492 }
493 retry_num_search_out:
494 if (*pnum != rpnum) {
495 device_printf(d->dev,
496 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
497 __func__, dirs, *pnum, rpnum);
498 *pnum = rpnum;
499 }
500 (*pnum)++;
501 snd_mtxunlock(d->lock);
502
503 ch->pid = -1;
504 ch->parentsnddev = d;
505 ch->parentchannel = parent;
506 ch->dev = d->dev;
507 snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
508
509 err = chn_init(ch, devinfo, dir, direction);
510 if (err) {
511 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
512 kobj_delete(ch->methods, M_DEVBUF);
513 free(ch, M_DEVBUF);
514 snd_mtxlock(d->lock);
515 (*pnum)--;
516 snd_mtxunlock(d->lock);
517
518 return NULL;
519 }
520
521 return ch;
522 }
523
524 int
525 pcm_chn_destroy(struct pcm_channel *ch)
526 {
527 struct snddev_info *d;
528 int err;
529
530 d = ch->parentsnddev;
531 err = chn_kill(ch);
532 if (err) {
533 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
534 return err;
535 }
536
537 kobj_delete(ch->methods, M_DEVBUF);
538 free(ch, M_DEVBUF);
539
540 return 0;
541 }
542
543 int
544 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
545 {
546 struct snddev_channel *sce, *tmp, *after;
547 unsigned rdevcount;
548 int device = device_get_unit(d->dev);
549 size_t namelen;
550
551 /*
552 * Note it's confusing nomenclature.
553 * dev_t
554 * device -> pcm_device
555 * unit -> pcm_channel
556 * channel -> snddev_channel
557 * device_t
558 * unit -> pcm_device
559 */
560
561 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
562 if (!sce) {
563 return ENOMEM;
564 }
565
566 snd_mtxlock(d->lock);
567 sce->channel = ch;
568 sce->chan_num = 0;
569 rdevcount = 0;
570 after = NULL;
571 SLIST_FOREACH(tmp, &d->channels, link) {
572 if (sce->chan_num == tmp->chan_num)
573 sce->chan_num++;
574 else {
575 #if 0
576 device_printf(d->dev,
577 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
578 __func__, sce->chan_num, tmp->chan_num);
579 #endif
580 goto retry_chan_num_search;
581 }
582 after = tmp;
583 rdevcount++;
584 }
585 goto retry_chan_num_search_out;
586 retry_chan_num_search:
587 /*
588 * Look for possible channel numbering collision. This may not
589 * be optimized, but it will ensure that no collision occured.
590 * Can be considered cheap since none of the locking/unlocking
591 * operations involved.
592 */
593 rdevcount = 0;
594 after = NULL;
595 SLIST_FOREACH(tmp, &d->channels, link) {
596 if (sce->chan_num == tmp->chan_num) {
597 sce->chan_num++;
598 goto retry_chan_num_search;
599 }
600 if (sce->chan_num > tmp->chan_num)
601 after = tmp;
602 rdevcount++;
603 }
604 retry_chan_num_search_out:
605 /*
606 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
607 */
608 if (sce->chan_num > PCMMAXCHAN) {
609 snd_mtxunlock(d->lock);
610 device_printf(d->dev,
611 "%s: WARNING: sce->chan_num overflow! (%d)\n",
612 __func__, sce->chan_num);
613 free(sce, M_DEVBUF);
614 return E2BIG;
615 }
616 if (d->devcount != rdevcount) {
617 device_printf(d->dev,
618 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
619 __func__, d->devcount, rdevcount);
620 d->devcount = rdevcount;
621 }
622 d->devcount++;
623 if (after == NULL) {
624 SLIST_INSERT_HEAD(&d->channels, sce, link);
625 } else {
626 SLIST_INSERT_AFTER(after, sce, link);
627 }
628 #if 0
629 if (1) {
630 int cnum = 0;
631 SLIST_FOREACH(tmp, &d->channels, link) {
632 if (cnum != tmp->chan_num)
633 device_printf(d->dev,
634 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
635 __func__, cnum, tmp->chan_num);
636 cnum++;
637 }
638 }
639 #endif
640
641 namelen = strlen(ch->name);
642 if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
643 snprintf(ch->name + namelen,
644 CHN_NAMELEN - namelen, ":dsp%d.%d",
645 device, sce->chan_num);
646 }
647 snd_mtxunlock(d->lock);
648
649 /*
650 * I will revisit these someday, and nuke it mercilessly..
651 */
652 sce->dsp_devt = make_dev(&dsp_cdevsw,
653 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
654 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
655 device, sce->chan_num);
656
657 sce->dspW_devt = make_dev(&dsp_cdevsw,
658 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
659 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
660 device, sce->chan_num);
661
662 sce->audio_devt = make_dev(&dsp_cdevsw,
663 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
664 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
665 device, sce->chan_num);
666
667 if (ch->direction == PCMDIR_REC)
668 sce->dspr_devt = make_dev(&dsp_cdevsw,
669 PCMMKMINOR(device, SND_DEV_DSPREC,
670 sce->chan_num), UID_ROOT, GID_WHEEL,
671 0666, "dspr%d.%d", device, sce->chan_num);
672
673 return 0;
674 }
675
676 int
677 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
678 {
679 struct snddev_channel *sce;
680 #if 0
681 int ourlock;
682
683 ourlock = 0;
684 if (!mtx_owned(d->lock)) {
685 snd_mtxlock(d->lock);
686 ourlock = 1;
687 }
688 #endif
689
690 SLIST_FOREACH(sce, &d->channels, link) {
691 if (sce->channel == ch)
692 goto gotit;
693 }
694 #if 0
695 if (ourlock)
696 snd_mtxunlock(d->lock);
697 #endif
698 return EINVAL;
699 gotit:
700 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
701
702 if (ch->flags & CHN_F_VIRTUAL)
703 d->vchancount--;
704 else if (ch->direction == PCMDIR_REC)
705 d->reccount--;
706 else
707 d->playcount--;
708
709 #if 0
710 if (ourlock)
711 snd_mtxunlock(d->lock);
712 #endif
713 free(sce, M_DEVBUF);
714
715 return 0;
716 }
717
718 int
719 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
720 {
721 struct snddev_info *d = device_get_softc(dev);
722 struct pcm_channel *ch;
723 int err;
724
725 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
726 if (!ch) {
727 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
728 return ENODEV;
729 }
730
731 err = pcm_chn_add(d, ch);
732 if (err) {
733 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
734 pcm_chn_destroy(ch);
735 return err;
736 }
737
738 return err;
739 }
740
741 static int
742 pcm_killchan(device_t dev)
743 {
744 struct snddev_info *d = device_get_softc(dev);
745 struct snddev_channel *sce;
746 struct pcm_channel *ch;
747 int error = 0;
748
749 sce = SLIST_FIRST(&d->channels);
750 ch = sce->channel;
751
752 error = pcm_chn_remove(d, sce->channel);
753 if (error)
754 return (error);
755 return (pcm_chn_destroy(ch));
756 }
757
758 int
759 pcm_setstatus(device_t dev, char *str)
760 {
761 struct snddev_info *d = device_get_softc(dev);
762
763 snd_mtxlock(d->lock);
764 strncpy(d->status, str, SND_STATUSLEN);
765 snd_mtxunlock(d->lock);
766 if (snd_maxautovchans > 0)
767 pcm_setvchans(d, 1);
768 return 0;
769 }
770
771 uint32_t
772 pcm_getflags(device_t dev)
773 {
774 struct snddev_info *d = device_get_softc(dev);
775
776 return d->flags;
777 }
778
779 void
780 pcm_setflags(device_t dev, uint32_t val)
781 {
782 struct snddev_info *d = device_get_softc(dev);
783
784 d->flags = val;
785 }
786
787 void *
788 pcm_getdevinfo(device_t dev)
789 {
790 struct snddev_info *d = device_get_softc(dev);
791
792 return d->devinfo;
793 }
794
795 unsigned int
796 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
797 {
798 struct snddev_info *d = device_get_softc(dev);
799 int sz, x;
800
801 sz = 0;
802 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
803 x = sz;
804 RANGE(sz, min, max);
805 if (x != sz)
806 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
807 x = min;
808 while (x < sz)
809 x <<= 1;
810 if (x > sz)
811 x >>= 1;
812 if (x != sz) {
813 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
814 sz = x;
815 }
816 } else {
817 sz = deflt;
818 }
819
820 d->bufsz = sz;
821
822 return sz;
823 }
824
825 int
826 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
827 {
828 struct snddev_info *d = device_get_softc(dev);
829
830 if (pcm_veto_load) {
831 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
832
833 return EINVAL;
834 }
835
836 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
837
838 #if 0
839 /*
840 * d->flags should be cleared by the allocator of the softc.
841 * We cannot clear this field here because several devices set
842 * this flag before calling pcm_register().
843 */
844 d->flags = 0;
845 #endif
846 d->dev = dev;
847 d->devinfo = devinfo;
848 d->devcount = 0;
849 d->reccount = 0;
850 d->playcount = 0;
851 d->vchancount = 0;
852 d->inprog = 0;
853
854 SLIST_INIT(&d->channels);
855
856 if ((numplay == 0 || numrec == 0) && numplay != numrec)
857 d->flags |= SD_F_SIMPLEX;
858
859 d->fakechan = fkchan_setup(dev);
860 chn_init(d->fakechan, NULL, 0, 0);
861
862 #ifdef SND_DYNSYSCTL
863 sysctl_ctx_init(&d->sysctl_tree);
864 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
865 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
866 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
867 if (d->sysctl_tree_top == NULL) {
868 sysctl_ctx_free(&d->sysctl_tree);
869 goto no;
870 }
871 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
872 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
873 #endif
874 if (numplay > 0) {
875 d->flags |= SD_F_AUTOVCHAN;
876 vchan_initsys(dev);
877 }
878
879 sndstat_register(dev, d->status, sndstat_prepare_pcm);
880 return 0;
881 no:
882 snd_mtxfree(d->lock);
883 return ENXIO;
884 }
885
886 int
887 pcm_unregister(device_t dev)
888 {
889 struct snddev_info *d = device_get_softc(dev);
890 struct snddev_channel *sce;
891 struct pcmchan_children *pce;
892 struct pcm_channel *ch;
893
894 if (sndstat_acquire() != 0) {
895 device_printf(dev, "unregister: sndstat busy\n");
896 return EBUSY;
897 }
898
899 snd_mtxlock(d->lock);
900 if (d->inprog) {
901 device_printf(dev, "unregister: operation in progress\n");
902 snd_mtxunlock(d->lock);
903 sndstat_release();
904 return EBUSY;
905 }
906
907 SLIST_FOREACH(sce, &d->channels, link) {
908 ch = sce->channel;
909 if (ch->refcount > 0) {
910 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
911 snd_mtxunlock(d->lock);
912 sndstat_release();
913 return EBUSY;
914 }
915 }
916
917 if (mixer_uninit(dev) == EBUSY) {
918 device_printf(dev, "unregister: mixer busy\n");
919 snd_mtxunlock(d->lock);
920 sndstat_release();
921 return EBUSY;
922 }
923
924 SLIST_FOREACH(sce, &d->channels, link) {
925 if (sce->dsp_devt) {
926 destroy_dev(sce->dsp_devt);
927 sce->dsp_devt = NULL;
928 }
929 if (sce->dspW_devt) {
930 destroy_dev(sce->dspW_devt);
931 sce->dspW_devt = NULL;
932 }
933 if (sce->audio_devt) {
934 destroy_dev(sce->audio_devt);
935 sce->audio_devt = NULL;
936 }
937 if (sce->dspr_devt) {
938 destroy_dev(sce->dspr_devt);
939 sce->dspr_devt = NULL;
940 }
941 d->devcount--;
942 ch = sce->channel;
943 if (ch == NULL)
944 continue;
945 pce = SLIST_FIRST(&ch->children);
946 while (pce != NULL) {
947 #if 0
948 device_printf(d->dev, "<%s> removing <%s>\n",
949 ch->name, (pce->channel != NULL) ?
950 pce->channel->name : "unknown");
951 #endif
952 SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
953 free(pce, M_DEVBUF);
954 pce = SLIST_FIRST(&ch->children);
955 }
956 }
957
958 #ifdef SND_DYNSYSCTL
959 d->sysctl_tree_top = NULL;
960 sysctl_ctx_free(&d->sysctl_tree);
961 #endif
962
963 #if 0
964 SLIST_FOREACH(sce, &d->channels, link) {
965 ch = sce->channel;
966 if (ch == NULL)
967 continue;
968 if (!SLIST_EMPTY(&ch->children))
969 device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
970 __func__, ch->name);
971 }
972 #endif
973 while (!SLIST_EMPTY(&d->channels))
974 pcm_killchan(dev);
975
976 chn_kill(d->fakechan);
977 fkchan_kill(d->fakechan);
978
979 #if 0
980 device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
981 "reccount=%u, vchancount=%u\n",
982 __func__, d->devcount, d->playcount, d->reccount,
983 d->vchancount);
984 #endif
985 snd_mtxunlock(d->lock);
986 snd_mtxfree(d->lock);
987 sndstat_unregister(dev);
988 sndstat_release();
989 return 0;
990 }
991
992 /************************************************************************/
993
994 static int
995 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
996 {
997 struct snddev_info *d;
998 struct snddev_channel *sce;
999 struct pcm_channel *c;
1000 struct pcm_feeder *f;
1001 int pc, rc, vc;
1002
1003 if (verbose < 1)
1004 return 0;
1005
1006 d = device_get_softc(dev);
1007 if (!d)
1008 return ENXIO;
1009
1010 snd_mtxlock(d->lock);
1011 if (!SLIST_EMPTY(&d->channels)) {
1012 pc = rc = vc = 0;
1013 SLIST_FOREACH(sce, &d->channels, link) {
1014 c = sce->channel;
1015 if (c->direction == PCMDIR_PLAY) {
1016 if (c->flags & CHN_F_VIRTUAL)
1017 vc++;
1018 else
1019 pc++;
1020 } else
1021 rc++;
1022 }
1023 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1024 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1025 #ifdef USING_DEVFS
1026 (device_get_unit(dev) == snd_unit)? " default" : ""
1027 #else
1028 ""
1029 #endif
1030 );
1031
1032 if (verbose <= 1) {
1033 snd_mtxunlock(d->lock);
1034 return 0;
1035 }
1036
1037 SLIST_FOREACH(sce, &d->channels, link) {
1038 c = sce->channel;
1039
1040 KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1041 ("hosed pcm channel setup"));
1042
1043 sbuf_printf(s, "\n\t");
1044
1045 /* it would be better to indent child channels */
1046 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1047 sbuf_printf(s, "spd %d", c->speed);
1048 if (c->speed != sndbuf_getspd(c->bufhard))
1049 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1050 sbuf_printf(s, ", fmt 0x%08x", c->format);
1051 if (c->format != sndbuf_getfmt(c->bufhard))
1052 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1053 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1054 if (c->pid != -1)
1055 sbuf_printf(s, ", pid %d", c->pid);
1056 sbuf_printf(s, "\n\t");
1057
1058 sbuf_printf(s, "interrupts %d, ", c->interrupts);
1059 if (c->direction == PCMDIR_REC)
1060 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1061 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1062 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1063 sndbuf_getblkcnt(c->bufhard),
1064 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1065 sndbuf_getblkcnt(c->bufsoft));
1066 else
1067 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1068 c->xruns, sndbuf_getready(c->bufsoft),
1069 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1070 sndbuf_getblkcnt(c->bufhard),
1071 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1072 sndbuf_getblkcnt(c->bufsoft));
1073 sbuf_printf(s, "\n\t");
1074
1075 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1076 sbuf_printf(s, " -> ");
1077 f = c->feeder;
1078 while (f->source != NULL)
1079 f = f->source;
1080 while (f != NULL) {
1081 sbuf_printf(s, "%s", f->class->name);
1082 if (f->desc->type == FEEDER_FMT)
1083 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1084 if (f->desc->type == FEEDER_RATE)
1085 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1086 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1087 f->desc->type == FEEDER_VOLUME)
1088 sbuf_printf(s, "(0x%08x)", f->desc->out);
1089 sbuf_printf(s, " -> ");
1090 f = f->parent;
1091 }
1092 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1093 }
1094 } else
1095 sbuf_printf(s, " (mixer only)");
1096 snd_mtxunlock(d->lock);
1097
1098 return 0;
1099 }
1100
1101 /************************************************************************/
1102
1103 #ifdef SND_DYNSYSCTL
1104 int
1105 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1106 {
1107 struct snddev_info *d;
1108 int err, newcnt;
1109
1110 d = oidp->oid_arg1;
1111
1112 newcnt = d->vchancount;
1113 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1114
1115 if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1116 err = pcm_setvchans(d, newcnt);
1117
1118 return err;
1119 }
1120 #endif
1121
1122 /************************************************************************/
1123
1124 static int
1125 sound_modevent(module_t mod, int type, void *data)
1126 {
1127 #if 0
1128 return (midi_modevent(mod, type, data));
1129 #else
1130 return 0;
1131 #endif
1132 }
1133
1134 DEV_MODULE(sound, sound_modevent, NULL);
1135 MODULE_VERSION(sound, SOUND_MODVER);
Cache object: 13f593b93b7b84275129ce0cf9101092
|