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