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