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