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