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.2/sys/dev/sound/pcm/sound.c 157495 2006-04-04 17:43:49Z 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 = 0;
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 || unit >= devclass_get_maxunit(pcm_devclass))
376 return EINVAL;
377 d = devclass_get_softc(pcm_devclass, unit);
378 if (d == NULL || SLIST_EMPTY(&d->channels))
379 return EINVAL;
380 snd_unit = unit;
381 }
382 return (error);
383 }
384 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
385 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
386 #endif
387
388 static int
389 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
390 {
391 struct snddev_info *d;
392 int i, v, error;
393
394 v = snd_maxautovchans;
395 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
396 if (error == 0 && req->newptr != NULL) {
397 if (v < 0 || v > PCMMAXCHAN)
398 return E2BIG;
399 if (pcm_devclass != NULL && v != snd_maxautovchans) {
400 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
401 d = devclass_get_softc(pcm_devclass, i);
402 if (!d)
403 continue;
404 pcm_setmaxautovchans(d, v);
405 }
406 }
407 snd_maxautovchans = v;
408 }
409 return (error);
410 }
411 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
412 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
413
414 struct pcm_channel *
415 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
416 {
417 struct snddev_channel *sce;
418 struct pcm_channel *ch, *c;
419 char *dirs;
420 uint32_t flsearch = 0;
421 int direction, err, rpnum, *pnum;
422
423 switch(dir) {
424 case PCMDIR_PLAY:
425 dirs = "play";
426 direction = PCMDIR_PLAY;
427 pnum = &d->playcount;
428 break;
429
430 case PCMDIR_REC:
431 dirs = "record";
432 direction = PCMDIR_REC;
433 pnum = &d->reccount;
434 break;
435
436 case PCMDIR_VIRTUAL:
437 dirs = "virtual";
438 direction = PCMDIR_PLAY;
439 pnum = &d->vchancount;
440 flsearch = CHN_F_VIRTUAL;
441 break;
442
443 default:
444 return NULL;
445 }
446
447 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
448 if (!ch)
449 return NULL;
450
451 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
452 if (!ch->methods) {
453 free(ch, M_DEVBUF);
454
455 return NULL;
456 }
457
458 snd_mtxlock(d->lock);
459 ch->num = 0;
460 rpnum = 0;
461 SLIST_FOREACH(sce, &d->channels, link) {
462 c = sce->channel;
463 if (direction != c->direction ||
464 (c->flags & CHN_F_VIRTUAL) != flsearch)
465 continue;
466 if (ch->num == c->num)
467 ch->num++;
468 else {
469 #if 0
470 device_printf(d->dev,
471 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
472 __func__, dirs, ch->num, c->num);
473 #endif
474 goto retry_num_search;
475 }
476 rpnum++;
477 }
478 goto retry_num_search_out;
479 retry_num_search:
480 rpnum = 0;
481 SLIST_FOREACH(sce, &d->channels, link) {
482 c = sce->channel;
483 if (direction != c->direction ||
484 (c->flags & CHN_F_VIRTUAL) != flsearch)
485 continue;
486 if (ch->num == c->num) {
487 ch->num++;
488 goto retry_num_search;
489 }
490 rpnum++;
491 }
492 retry_num_search_out:
493 if (*pnum != rpnum) {
494 device_printf(d->dev,
495 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
496 __func__, dirs, *pnum, rpnum);
497 *pnum = rpnum;
498 }
499 (*pnum)++;
500 snd_mtxunlock(d->lock);
501
502 ch->pid = -1;
503 ch->parentsnddev = d;
504 ch->parentchannel = parent;
505 ch->dev = d->dev;
506 snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
507
508 err = chn_init(ch, devinfo, dir, direction);
509 if (err) {
510 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
511 kobj_delete(ch->methods, M_DEVBUF);
512 free(ch, M_DEVBUF);
513 snd_mtxlock(d->lock);
514 (*pnum)--;
515 snd_mtxunlock(d->lock);
516
517 return NULL;
518 }
519
520 return ch;
521 }
522
523 int
524 pcm_chn_destroy(struct pcm_channel *ch)
525 {
526 struct snddev_info *d;
527 int err;
528
529 d = ch->parentsnddev;
530 err = chn_kill(ch);
531 if (err) {
532 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
533 return err;
534 }
535
536 kobj_delete(ch->methods, M_DEVBUF);
537 free(ch, M_DEVBUF);
538
539 return 0;
540 }
541
542 int
543 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
544 {
545 struct snddev_channel *sce, *tmp, *after;
546 unsigned rdevcount;
547 int device = device_get_unit(d->dev);
548 size_t namelen;
549
550 /*
551 * Note it's confusing nomenclature.
552 * dev_t
553 * device -> pcm_device
554 * unit -> pcm_channel
555 * channel -> snddev_channel
556 * device_t
557 * unit -> pcm_device
558 */
559
560 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
561 if (!sce) {
562 return ENOMEM;
563 }
564
565 snd_mtxlock(d->lock);
566 sce->channel = ch;
567 sce->chan_num = 0;
568 rdevcount = 0;
569 after = NULL;
570 SLIST_FOREACH(tmp, &d->channels, link) {
571 if (sce->chan_num == tmp->chan_num)
572 sce->chan_num++;
573 else {
574 #if 0
575 device_printf(d->dev,
576 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
577 __func__, sce->chan_num, tmp->chan_num);
578 #endif
579 goto retry_chan_num_search;
580 }
581 after = tmp;
582 rdevcount++;
583 }
584 goto retry_chan_num_search_out;
585 retry_chan_num_search:
586 /*
587 * Look for possible channel numbering collision. This may not
588 * be optimized, but it will ensure that no collision occured.
589 * Can be considered cheap since none of the locking/unlocking
590 * operations involved.
591 */
592 rdevcount = 0;
593 after = NULL;
594 SLIST_FOREACH(tmp, &d->channels, link) {
595 if (sce->chan_num == tmp->chan_num) {
596 sce->chan_num++;
597 goto retry_chan_num_search;
598 }
599 if (sce->chan_num > tmp->chan_num)
600 after = tmp;
601 rdevcount++;
602 }
603 retry_chan_num_search_out:
604 /*
605 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
606 */
607 if (sce->chan_num > PCMMAXCHAN) {
608 snd_mtxunlock(d->lock);
609 device_printf(d->dev,
610 "%s: WARNING: sce->chan_num overflow! (%d)\n",
611 __func__, sce->chan_num);
612 free(sce, M_DEVBUF);
613 return E2BIG;
614 }
615 if (d->devcount != rdevcount) {
616 device_printf(d->dev,
617 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
618 __func__, d->devcount, rdevcount);
619 d->devcount = rdevcount;
620 }
621 d->devcount++;
622 if (after == NULL) {
623 SLIST_INSERT_HEAD(&d->channels, sce, link);
624 } else {
625 SLIST_INSERT_AFTER(after, sce, link);
626 }
627 #if 0
628 if (1) {
629 int cnum = 0;
630 SLIST_FOREACH(tmp, &d->channels, link) {
631 if (cnum != tmp->chan_num)
632 device_printf(d->dev,
633 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
634 __func__, cnum, tmp->chan_num);
635 cnum++;
636 }
637 }
638 #endif
639
640 namelen = strlen(ch->name);
641 if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
642 snprintf(ch->name + namelen,
643 CHN_NAMELEN - namelen, ":dsp%d.%d",
644 device, sce->chan_num);
645 }
646 snd_mtxunlock(d->lock);
647
648 /*
649 * I will revisit these someday, and nuke it mercilessly..
650 */
651 sce->dsp_devt = make_dev(&dsp_cdevsw,
652 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
653 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
654 device, sce->chan_num);
655
656 sce->dspW_devt = make_dev(&dsp_cdevsw,
657 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
658 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
659 device, sce->chan_num);
660
661 sce->audio_devt = make_dev(&dsp_cdevsw,
662 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
663 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
664 device, sce->chan_num);
665
666 if (ch->direction == PCMDIR_REC)
667 sce->dspr_devt = make_dev(&dsp_cdevsw,
668 PCMMKMINOR(device, SND_DEV_DSPREC,
669 sce->chan_num), UID_ROOT, GID_WHEEL,
670 0666, "dspr%d.%d", device, sce->chan_num);
671
672 return 0;
673 }
674
675 int
676 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
677 {
678 struct snddev_channel *sce;
679 #if 0
680 int ourlock;
681
682 ourlock = 0;
683 if (!mtx_owned(d->lock)) {
684 snd_mtxlock(d->lock);
685 ourlock = 1;
686 }
687 #endif
688
689 SLIST_FOREACH(sce, &d->channels, link) {
690 if (sce->channel == ch)
691 goto gotit;
692 }
693 #if 0
694 if (ourlock)
695 snd_mtxunlock(d->lock);
696 #endif
697 return EINVAL;
698 gotit:
699 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
700
701 if (ch->flags & CHN_F_VIRTUAL)
702 d->vchancount--;
703 else if (ch->direction == PCMDIR_REC)
704 d->reccount--;
705 else
706 d->playcount--;
707
708 #if 0
709 if (ourlock)
710 snd_mtxunlock(d->lock);
711 #endif
712 free(sce, M_DEVBUF);
713
714 return 0;
715 }
716
717 int
718 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
719 {
720 struct snddev_info *d = device_get_softc(dev);
721 struct pcm_channel *ch;
722 int err;
723
724 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
725 if (!ch) {
726 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
727 return ENODEV;
728 }
729
730 err = pcm_chn_add(d, ch);
731 if (err) {
732 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
733 pcm_chn_destroy(ch);
734 return err;
735 }
736
737 return err;
738 }
739
740 static int
741 pcm_killchan(device_t dev)
742 {
743 struct snddev_info *d = device_get_softc(dev);
744 struct snddev_channel *sce;
745 struct pcm_channel *ch;
746 int error = 0;
747
748 sce = SLIST_FIRST(&d->channels);
749 ch = sce->channel;
750
751 error = pcm_chn_remove(d, sce->channel);
752 if (error)
753 return (error);
754 return (pcm_chn_destroy(ch));
755 }
756
757 int
758 pcm_setstatus(device_t dev, char *str)
759 {
760 struct snddev_info *d = device_get_softc(dev);
761
762 snd_mtxlock(d->lock);
763 strncpy(d->status, str, SND_STATUSLEN);
764 snd_mtxunlock(d->lock);
765 if (snd_maxautovchans > 0)
766 pcm_setvchans(d, 1);
767 return 0;
768 }
769
770 uint32_t
771 pcm_getflags(device_t dev)
772 {
773 struct snddev_info *d = device_get_softc(dev);
774
775 return d->flags;
776 }
777
778 void
779 pcm_setflags(device_t dev, uint32_t val)
780 {
781 struct snddev_info *d = device_get_softc(dev);
782
783 d->flags = val;
784 }
785
786 void *
787 pcm_getdevinfo(device_t dev)
788 {
789 struct snddev_info *d = device_get_softc(dev);
790
791 return d->devinfo;
792 }
793
794 unsigned int
795 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
796 {
797 struct snddev_info *d = device_get_softc(dev);
798 int sz, x;
799
800 sz = 0;
801 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
802 x = sz;
803 RANGE(sz, min, max);
804 if (x != sz)
805 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
806 x = min;
807 while (x < sz)
808 x <<= 1;
809 if (x > sz)
810 x >>= 1;
811 if (x != sz) {
812 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
813 sz = x;
814 }
815 } else {
816 sz = deflt;
817 }
818
819 d->bufsz = sz;
820
821 return sz;
822 }
823
824 int
825 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
826 {
827 struct snddev_info *d = device_get_softc(dev);
828
829 if (pcm_veto_load) {
830 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
831
832 return EINVAL;
833 }
834
835 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
836
837 #if 0
838 /*
839 * d->flags should be cleared by the allocator of the softc.
840 * We cannot clear this field here because several devices set
841 * this flag before calling pcm_register().
842 */
843 d->flags = 0;
844 #endif
845 d->dev = dev;
846 d->devinfo = devinfo;
847 d->devcount = 0;
848 d->reccount = 0;
849 d->playcount = 0;
850 d->vchancount = 0;
851 d->inprog = 0;
852
853 SLIST_INIT(&d->channels);
854
855 if ((numplay == 0 || numrec == 0) && numplay != numrec)
856 d->flags |= SD_F_SIMPLEX;
857
858 d->fakechan = fkchan_setup(dev);
859 chn_init(d->fakechan, NULL, 0, 0);
860
861 #ifdef SND_DYNSYSCTL
862 sysctl_ctx_init(&d->sysctl_tree);
863 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
864 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
865 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
866 if (d->sysctl_tree_top == NULL) {
867 sysctl_ctx_free(&d->sysctl_tree);
868 goto no;
869 }
870 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
871 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
872 #endif
873 if (numplay > 0) {
874 d->flags |= SD_F_AUTOVCHAN;
875 vchan_initsys(dev);
876 }
877
878 sndstat_register(dev, d->status, sndstat_prepare_pcm);
879 return 0;
880 no:
881 snd_mtxfree(d->lock);
882 return ENXIO;
883 }
884
885 int
886 pcm_unregister(device_t dev)
887 {
888 struct snddev_info *d = device_get_softc(dev);
889 struct snddev_channel *sce;
890 struct pcmchan_children *pce;
891 struct pcm_channel *ch;
892
893 if (sndstat_acquire() != 0) {
894 device_printf(dev, "unregister: sndstat busy\n");
895 return EBUSY;
896 }
897
898 snd_mtxlock(d->lock);
899 if (d->inprog) {
900 device_printf(dev, "unregister: operation in progress\n");
901 snd_mtxunlock(d->lock);
902 sndstat_release();
903 return EBUSY;
904 }
905
906 SLIST_FOREACH(sce, &d->channels, link) {
907 ch = sce->channel;
908 if (ch->refcount > 0) {
909 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
910 snd_mtxunlock(d->lock);
911 sndstat_release();
912 return EBUSY;
913 }
914 }
915
916 if (mixer_uninit(dev) == EBUSY) {
917 device_printf(dev, "unregister: mixer busy\n");
918 snd_mtxunlock(d->lock);
919 sndstat_release();
920 return EBUSY;
921 }
922
923 SLIST_FOREACH(sce, &d->channels, link) {
924 if (sce->dsp_devt) {
925 destroy_dev(sce->dsp_devt);
926 sce->dsp_devt = NULL;
927 }
928 if (sce->dspW_devt) {
929 destroy_dev(sce->dspW_devt);
930 sce->dspW_devt = NULL;
931 }
932 if (sce->audio_devt) {
933 destroy_dev(sce->audio_devt);
934 sce->audio_devt = NULL;
935 }
936 if (sce->dspr_devt) {
937 destroy_dev(sce->dspr_devt);
938 sce->dspr_devt = NULL;
939 }
940 d->devcount--;
941 ch = sce->channel;
942 if (ch == NULL)
943 continue;
944 pce = SLIST_FIRST(&ch->children);
945 while (pce != NULL) {
946 #if 0
947 device_printf(d->dev, "<%s> removing <%s>\n",
948 ch->name, (pce->channel != NULL) ?
949 pce->channel->name : "unknown");
950 #endif
951 SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
952 free(pce, M_DEVBUF);
953 pce = SLIST_FIRST(&ch->children);
954 }
955 }
956
957 #ifdef SND_DYNSYSCTL
958 d->sysctl_tree_top = NULL;
959 sysctl_ctx_free(&d->sysctl_tree);
960 #endif
961
962 #if 0
963 SLIST_FOREACH(sce, &d->channels, link) {
964 ch = sce->channel;
965 if (ch == NULL)
966 continue;
967 if (!SLIST_EMPTY(&ch->children))
968 device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
969 __func__, ch->name);
970 }
971 #endif
972 while (!SLIST_EMPTY(&d->channels))
973 pcm_killchan(dev);
974
975 chn_kill(d->fakechan);
976 fkchan_kill(d->fakechan);
977
978 #if 0
979 device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
980 "reccount=%u, vchancount=%u\n",
981 __func__, d->devcount, d->playcount, d->reccount,
982 d->vchancount);
983 #endif
984 snd_mtxunlock(d->lock);
985 snd_mtxfree(d->lock);
986 sndstat_unregister(dev);
987 sndstat_release();
988 return 0;
989 }
990
991 /************************************************************************/
992
993 static int
994 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
995 {
996 struct snddev_info *d;
997 struct snddev_channel *sce;
998 struct pcm_channel *c;
999 struct pcm_feeder *f;
1000 int pc, rc, vc;
1001
1002 if (verbose < 1)
1003 return 0;
1004
1005 d = device_get_softc(dev);
1006 if (!d)
1007 return ENXIO;
1008
1009 snd_mtxlock(d->lock);
1010 if (!SLIST_EMPTY(&d->channels)) {
1011 pc = rc = vc = 0;
1012 SLIST_FOREACH(sce, &d->channels, link) {
1013 c = sce->channel;
1014 if (c->direction == PCMDIR_PLAY) {
1015 if (c->flags & CHN_F_VIRTUAL)
1016 vc++;
1017 else
1018 pc++;
1019 } else
1020 rc++;
1021 }
1022 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1023 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1024 #ifdef USING_DEVFS
1025 (device_get_unit(dev) == snd_unit)? " default" : ""
1026 #else
1027 ""
1028 #endif
1029 );
1030
1031 if (verbose <= 1) {
1032 snd_mtxunlock(d->lock);
1033 return 0;
1034 }
1035
1036 SLIST_FOREACH(sce, &d->channels, link) {
1037 c = sce->channel;
1038
1039 KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1040 ("hosed pcm channel setup"));
1041
1042 sbuf_printf(s, "\n\t");
1043
1044 /* it would be better to indent child channels */
1045 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1046 sbuf_printf(s, "spd %d", c->speed);
1047 if (c->speed != sndbuf_getspd(c->bufhard))
1048 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1049 sbuf_printf(s, ", fmt 0x%08x", c->format);
1050 if (c->format != sndbuf_getfmt(c->bufhard))
1051 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1052 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1053 if (c->pid != -1)
1054 sbuf_printf(s, ", pid %d", c->pid);
1055 sbuf_printf(s, "\n\t");
1056
1057 sbuf_printf(s, "interrupts %d, ", c->interrupts);
1058 if (c->direction == PCMDIR_REC)
1059 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1060 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1061 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1062 sndbuf_getblkcnt(c->bufhard),
1063 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1064 sndbuf_getblkcnt(c->bufsoft));
1065 else
1066 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1067 c->xruns, sndbuf_getready(c->bufsoft),
1068 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1069 sndbuf_getblkcnt(c->bufhard),
1070 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1071 sndbuf_getblkcnt(c->bufsoft));
1072 sbuf_printf(s, "\n\t");
1073
1074 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1075 sbuf_printf(s, " -> ");
1076 f = c->feeder;
1077 while (f->source != NULL)
1078 f = f->source;
1079 while (f != NULL) {
1080 sbuf_printf(s, "%s", f->class->name);
1081 if (f->desc->type == FEEDER_FMT)
1082 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1083 if (f->desc->type == FEEDER_RATE)
1084 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1085 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1086 f->desc->type == FEEDER_VOLUME)
1087 sbuf_printf(s, "(0x%08x)", f->desc->out);
1088 sbuf_printf(s, " -> ");
1089 f = f->parent;
1090 }
1091 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1092 }
1093 } else
1094 sbuf_printf(s, " (mixer only)");
1095 snd_mtxunlock(d->lock);
1096
1097 return 0;
1098 }
1099
1100 /************************************************************************/
1101
1102 #ifdef SND_DYNSYSCTL
1103 int
1104 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1105 {
1106 struct snddev_info *d;
1107 int err, newcnt;
1108
1109 d = oidp->oid_arg1;
1110
1111 newcnt = d->vchancount;
1112 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1113
1114 if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1115 err = pcm_setvchans(d, newcnt);
1116
1117 return err;
1118 }
1119 #endif
1120
1121 /************************************************************************/
1122
1123 static int
1124 sound_modevent(module_t mod, int type, void *data)
1125 {
1126 #if 0
1127 return (midi_modevent(mod, type, data));
1128 #else
1129 return 0;
1130 #endif
1131 }
1132
1133 DEV_MODULE(sound, sound_modevent, NULL);
1134 MODULE_VERSION(sound, SOUND_MODVER);
Cache object: ce489bc922cd49d9746b626c598a3a11
|