a4909ade46498d1ed61abc49061652a6c5e1e2d5
[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         tsleep_interlock(addr, flags);
132         snd_mtxunlock(lock);
133         r = tsleep(addr, flags | PINTERLOCKED, wmesg, timo);
134         snd_mtxlock(lock);
135         return(r);
136 }
137
138
139
140 int
141 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
142 {
143 #ifdef USING_MUTEX
144         flags &= INTR_MPSAFE;
145         flags |= INTR_TYPE_AV;
146 #else
147         flags = INTR_TYPE_AV;
148 #endif
149         return bus_setup_intr(dev, res, flags, hand, param, cookiep, NULL);
150 }
151
152 #ifndef PCM_DEBUG_MTX
153 void
154 pcm_lock(struct snddev_info *d)
155 {
156         snd_mtxlock(d->lock);
157 }
158
159 void
160 pcm_unlock(struct snddev_info *d)
161 {
162         snd_mtxunlock(d->lock);
163 }
164 #endif
165
166 struct pcm_channel *
167 pcm_getfakechan(struct snddev_info *d)
168 {
169         return d->fakechan;
170 }
171
172 static int
173 pcm_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;
210 addok:
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;
261 remok:
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
273 setvchans_out:
274         pcm_inprog(d, -1);
275         return err;
276 }
277
278 /* return error status and a locked channel */
279 int
280 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
281                 pid_t pid, int chnum)
282 {
283         struct pcm_channel *c;
284         struct snddev_channel *sce;
285         int err;
286
287 retry_chnalloc:
288         err = ENODEV;
289         /* scan for a free channel */
290         SLIST_FOREACH(sce, &d->channels, link) {
291                 c = sce->channel;
292                 CHN_LOCK(c);
293                 if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
294                         if (chnum < 0 || sce->chan_num == chnum) {
295                                 c->flags |= CHN_F_BUSY;
296                                 c->pid = pid;
297                                 *ch = c;
298                                 return 0;
299                         }
300                 }
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;
312                 CHN_UNLOCK(c);
313         }
314
315         /* no channel available */
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;
323                 }
324         }
325
326         return err;
327 }
328
329 /* release a locked channel and unlock it */
330 int
331 pcm_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
340 int
341 pcm_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
351 int
352 pcm_inprog(struct snddev_info *d, int delta)
353 {
354         int r;
355
356         if (delta == 0)
357                 return d->inprog;
358
359         /* backtrace(); */
360         pcm_lock(d);
361         d->inprog += delta;
362         r = d->inprog;
363         pcm_unlock(d);
364         return r;
365 }
366
367 static void
368 pcm_setmaxautovchans(struct snddev_info *d, int num)
369 {
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);
374 }
375
376 #ifdef USING_DEVFS
377 static int
378 sysctl_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) {
386                 if (unit < 0 || (pcm_devclass != NULL &&
387                     unit >= devclass_get_maxunit(pcm_devclass)))
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 }
396 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
397             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
398 #endif
399
400 static int
401 sysctl_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) {
409                 if (v < 0 || v > PCMMAXCHAN)
410                         return E2BIG;
411                 if (pcm_devclass != NULL && v != snd_maxautovchans) {
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 }
423 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
424             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
425
426 struct pcm_channel *
427 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
428 {
429         struct snddev_channel *sce;
430         struct pcm_channel *ch, *c;
431         char *dirs;
432         uint32_t flsearch = 0;
433         int direction, err, rpnum, *pnum;
434
435         switch(dir) {
436         case PCMDIR_PLAY:
437                 dirs = "play";
438                 direction = PCMDIR_PLAY;
439                 pnum = &d->playcount;
440                 break;
441
442         case PCMDIR_REC:
443                 dirs = "record";
444                 direction = PCMDIR_REC;
445                 pnum = &d->reccount;
446                 break;
447
448         case PCMDIR_VIRTUAL:
449                 dirs = "virtual";
450                 direction = PCMDIR_PLAY;
451                 pnum = &d->vchancount;
452                 flsearch = CHN_F_VIRTUAL;
453                 break;
454
455         default:
456                 return NULL;
457         }
458
459         ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
460
461         ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
462         if (!ch->methods) {
463                 kfree(ch, M_DEVBUF);
464
465                 return NULL;
466         }
467
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;
489 retry_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         }
502 retry_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);
511
512         ch->pid = -1;
513         ch->parentsnddev = d;
514         ch->parentchannel = parent;
515         ch->dev = d->dev;
516         ksnprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
517
518         err = chn_init(ch, devinfo, dir, direction);
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);
522                 kfree(ch, M_DEVBUF);
523                 snd_mtxlock(d->lock);
524                 (*pnum)--;
525                 snd_mtxunlock(d->lock);
526
527                 return NULL;
528         }
529
530         return ch;
531 }
532
533 struct pcm_channel *
534 pcm_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
549 int
550 pcm_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
562         kobj_delete(ch->methods, M_DEVBUF);
563         kfree(ch, M_DEVBUF);
564
565         return 0;
566 }
567
568 int
569 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
570 {
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          */
585
586         sce = kmalloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
587
588         snd_mtxlock(d->lock);
589         sce->channel = ch;
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;
608 retry_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         }
626 retry_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) {
646                 SLIST_INSERT_HEAD(&d->channels, sce, link);
647         } else {
648                 SLIST_INSERT_AFTER(after, sce, link);
649         }
650 #if 0
651         if (1) {
652                 int cnum = 0;
653                 SLIST_FOREACH(tmp, &d->channels, link) {
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++;
659                 }
660         }
661 #endif
662
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);
668         }
669         snd_mtxunlock(d->lock);
670
671         /*
672          * I will revisit these someday, and nuke it mercilessly..
673          */
674         dev_ops_add(&dsp_cdevsw,
675                     PCMMKMINOR(-1, -1, 0),
676                     PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
677         sce->dsp_devt = make_dev(&dsp_cdevsw,
678                         PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
679                         UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
680                         device, sce->chan_num);
681         reference_dev(sce->dsp_devt);
682
683         dev_ops_add(&dsp_cdevsw,
684                     PCMMKMINOR(-1, -1, 0),
685                     PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num));
686         sce->dspW_devt = make_dev(&dsp_cdevsw,
687                         PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
688                         UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
689                         device, sce->chan_num);
690         reference_dev(sce->dspW_devt);
691
692         /*
693         dev_ops_add(&dsp_cdevsw,
694                     PCMMKMINOR(-1, -1, 0),
695                     PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
696         sce->audio_devt = make_dev(&dsp_cdevsw,
697                         PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
698                         UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
699                         device, sce->chan_num);
700         */
701         sce->audio_devt = sce->dsp_devt;
702         reference_dev(sce->audio_devt);
703
704         if (ch->direction == PCMDIR_REC) {
705                 dev_ops_add(&dsp_cdevsw,
706                             PCMMKMINOR(-1, -1, 0),
707                             PCMMKMINOR(device, SND_DEV_DSPREC, sce->chan_num));
708                 sce->dspr_devt = make_dev(&dsp_cdevsw,
709                                 PCMMKMINOR(device, SND_DEV_DSPREC,
710                                         sce->chan_num), UID_ROOT, GID_WHEEL,
711                                 0666, "dspr%d.%d", device, sce->chan_num);
712                 reference_dev(sce->dspr_devt);
713         }
714
715         return 0;
716 }
717
718 int
719 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
720 {
721         struct snddev_channel *sce;
722 #if 0
723         int ourlock;
724
725         ourlock = 0;
726         if (!mtx_owned(d->lock)) {
727                 snd_mtxlock(d->lock);
728                 ourlock = 1;
729         }
730 #endif
731
732         SLIST_FOREACH(sce, &d->channels, link) {
733                 if (sce->channel == ch)
734                         goto gotit;
735         }
736 #if 0
737         if (ourlock)
738                 snd_mtxunlock(d->lock);
739 #endif
740         return EINVAL;
741 gotit:
742         SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
743
744         if (ch->flags & CHN_F_VIRTUAL)
745                 d->vchancount--;
746         else if (ch->direction == PCMDIR_REC)
747                 d->reccount--;
748         else
749                 d->playcount--;
750
751 #if 0
752         if (ourlock)
753                 snd_mtxunlock(d->lock);
754 #endif
755         kfree(sce, M_DEVBUF);
756
757         return 0;
758 }
759
760 int
761 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
762 {
763         struct snddev_info *d = device_get_softc(dev);
764         struct pcm_channel *ch;
765         int err;
766
767         ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
768         if (!ch) {
769                 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
770                 return ENODEV;
771         }
772
773         err = pcm_chn_add(d, ch);
774         if (err) {
775                 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
776                 pcm_chn_destroy(ch);
777                 return err;
778         }
779
780         return err;
781 }
782
783 static int
784 pcm_killchan(device_t dev)
785 {
786         struct snddev_info *d = device_get_softc(dev);
787         struct snddev_channel *sce;
788         struct pcm_channel *ch;
789         int error = 0;
790
791         sce = SLIST_FIRST(&d->channels);
792         ch = sce->channel;
793
794         error = pcm_chn_remove(d, sce->channel);
795         if (error)
796                 return (error);
797         return (pcm_chn_destroy(ch));
798 }
799
800 int
801 pcm_setstatus(device_t dev, char *str)
802 {
803         struct snddev_info *d = device_get_softc(dev);
804
805         snd_mtxlock(d->lock);
806         strncpy(d->status, str, SND_STATUSLEN);
807         snd_mtxunlock(d->lock);
808         if (snd_maxautovchans > 0)
809                 pcm_setvchans(d, 1);
810         return 0;
811 }
812
813 uint32_t
814 pcm_getflags(device_t dev)
815 {
816         struct snddev_info *d = device_get_softc(dev);
817
818         return d->flags;
819 }
820
821 void
822 pcm_setflags(device_t dev, uint32_t val)
823 {
824         struct snddev_info *d = device_get_softc(dev);
825
826         d->flags = val;
827 }
828
829 void *
830 pcm_getdevinfo(device_t dev)
831 {
832         struct snddev_info *d = device_get_softc(dev);
833
834         return d->devinfo;
835 }
836
837 unsigned int
838 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
839 {
840         struct snddev_info *d = device_get_softc(dev);
841         int sz, x;
842
843         sz = 0;
844         if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
845                 x = sz;
846                 RANGE(sz, min, max);
847                 if (x != sz)
848                         device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
849                 x = min;
850                 while (x < sz)
851                         x <<= 1;
852                 if (x > sz)
853                         x >>= 1;
854                 if (x != sz) {
855                         device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
856                         sz = x;
857                 }
858         } else {
859                 sz = deflt;
860         }
861
862         d->bufsz = sz;
863
864         return sz;
865 }
866
867 int
868 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
869 {
870         struct snddev_info *d = device_get_softc(dev);
871
872         if (pcm_veto_load) {
873                 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
874
875                 return EINVAL;
876         }
877
878         d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
879
880 #if 0
881         /*
882          * d->flags should be cleared by the allocator of the softc.
883          * We cannot clear this field here because several devices set
884          * this flag before calling pcm_register().
885          */
886         d->flags = 0;
887 #endif
888         d->dev = dev;
889         d->devinfo = devinfo;
890         d->devcount = 0;
891         d->reccount = 0;
892         d->playcount = 0;
893         d->vchancount = 0;
894         d->inprog = 0;
895
896         SLIST_INIT(&d->channels);
897
898         if ((numplay == 0 || numrec == 0) && numplay != numrec)
899                 d->flags |= SD_F_SIMPLEX;
900
901         d->fakechan = fkchan_setup(dev);
902         chn_init(d->fakechan, NULL, 0, 0);
903
904 #ifdef SND_DYNSYSCTL
905         sysctl_ctx_init(&d->sysctl_tree);
906         d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
907                                  SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
908                                  device_get_nameunit(dev), CTLFLAG_RD, 0, "");
909         if (d->sysctl_tree_top == NULL) {
910                 sysctl_ctx_free(&d->sysctl_tree);
911                 goto no;
912         }
913         SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
914             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
915 #endif
916         if (numplay > 0) {
917                 d->flags |= SD_F_AUTOVCHAN;
918                 vchan_initsys(dev);
919         }
920
921         sndstat_register(dev, d->status, sndstat_prepare_pcm);
922         return 0;
923 no:
924         snd_mtxfree(d->lock);
925         return ENXIO;
926 }
927
928 int
929 pcm_unregister(device_t dev)
930 {
931         struct snddev_info *d = device_get_softc(dev);
932         struct snddev_channel *sce;
933         struct pcmchan_children *pce;
934         struct pcm_channel *ch;
935
936         if (sndstat_acquire() != 0) {
937                 device_printf(dev, "unregister: sndstat busy\n");
938                 return EBUSY;
939         }
940
941         snd_mtxlock(d->lock);
942         if (d->inprog) {
943                 device_printf(dev, "unregister: operation in progress\n");
944                 snd_mtxunlock(d->lock);
945                 sndstat_release();
946                 return EBUSY;
947         }
948
949         SLIST_FOREACH(sce, &d->channels, link) {
950                 ch = sce->channel;
951                 if (ch->refcount > 0) {
952                         device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
953                         snd_mtxunlock(d->lock);
954                         sndstat_release();
955                         return EBUSY;
956                 }
957         }
958
959         if (mixer_uninit(dev) == EBUSY) {
960                 device_printf(dev, "unregister: mixer busy\n");
961                 snd_mtxunlock(d->lock);
962                 sndstat_release();
963                 return EBUSY;
964         }
965
966         SLIST_FOREACH(sce, &d->channels, link) {
967                 int unit = device_get_unit(d->dev);
968
969                 if (sce->dsp_devt) {
970                         release_dev(sce->dsp_devt);
971                         kprintf("devfs: Please check that only the correct dsp devices were removed!!!\n");
972                         dev_ops_remove_minor(&dsp_cdevsw,
973                                     /*PCMMKMINOR(-1, -1, 0),*/
974                                     PCMMKMINOR(unit, SND_DEV_DSP, sce->chan_num));
975                         sce->dsp_devt = NULL;
976                 }
977                 if (sce->dspW_devt) {
978                         release_dev(sce->dspW_devt);
979                         kprintf("devfs: Please check that only the correct dspW devices were removed!!!\n");
980                         dev_ops_remove_minor(&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                         kprintf("devfs: Please check that only the correct dspr devices were removed!!!!\n");
997                         dev_ops_remove_minor(&dsp_cdevsw,
998                                     /*PCMMKMINOR(-1, -1, 0),*/
999                                     PCMMKMINOR(unit, SND_DEV_DSPREC, sce->chan_num));
1000                         sce->dspr_devt = NULL;
1001                 }
1002                 d->devcount--;
1003                 ch = sce->channel;
1004                 if (ch == NULL)
1005                         continue;
1006                 pce = SLIST_FIRST(&ch->children);
1007                 while (pce != NULL) {
1008 #if 0
1009                         device_printf(d->dev, "<%s> removing <%s>\n",
1010                                 ch->name, (pce->channel != NULL) ?
1011                                         pce->channel->name : "unknown");
1012 #endif
1013                         SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
1014                         kfree(pce, M_DEVBUF);
1015                         pce = SLIST_FIRST(&ch->children);
1016                 }
1017         }
1018
1019 #ifdef SND_DYNSYSCTL
1020         d->sysctl_tree_top = NULL;
1021         sysctl_ctx_free(&d->sysctl_tree);
1022 #endif
1023
1024 #if 0
1025         SLIST_FOREACH(sce, &d->channels, link) {
1026                 ch = sce->channel;
1027                 if (ch == NULL)
1028                         continue;
1029                 if (!SLIST_EMPTY(&ch->children))
1030                         device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
1031                                 __func__, ch->name);
1032         }
1033 #endif
1034         while (!SLIST_EMPTY(&d->channels))
1035                 pcm_killchan(dev);
1036
1037         chn_kill(d->fakechan);
1038         fkchan_kill(d->fakechan);
1039
1040 #if 0
1041         device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
1042                 "reccount=%u, vchancount=%u\n",
1043                 __func__, d->devcount, d->playcount, d->reccount,
1044                 d->vchancount);
1045 #endif
1046         snd_mtxunlock(d->lock);
1047         snd_mtxfree(d->lock);
1048         sndstat_unregister(dev);
1049         sndstat_release();
1050         return 0;
1051 }
1052
1053 /************************************************************************/
1054
1055 static int
1056 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1057 {
1058         struct snddev_info *d;
1059         struct snddev_channel *sce;
1060         struct pcm_channel *c;
1061         struct pcm_feeder *f;
1062         int pc, rc, vc;
1063
1064         if (verbose < 1)
1065                 return 0;
1066
1067         d = device_get_softc(dev);
1068         if (!d)
1069                 return ENXIO;
1070
1071         snd_mtxlock(d->lock);
1072         if (!SLIST_EMPTY(&d->channels)) {
1073                 pc = rc = vc = 0;
1074                 SLIST_FOREACH(sce, &d->channels, link) {
1075                         c = sce->channel;
1076                         if (c->direction == PCMDIR_PLAY) {
1077                                 if (c->flags & CHN_F_VIRTUAL)
1078                                         vc++;
1079                                 else
1080                                         pc++;
1081                         } else
1082                                 rc++;
1083                 }
1084                 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1085                                 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1086 #ifdef USING_DEVFS
1087                                 (device_get_unit(dev) == snd_unit)? " default" : ""
1088 #else
1089                                 ""
1090 #endif
1091                                 );
1092
1093                 if (verbose <= 1) {
1094                         snd_mtxunlock(d->lock);
1095                         return 0;
1096                 }
1097
1098                 SLIST_FOREACH(sce, &d->channels, link) {
1099                         c = sce->channel;
1100
1101                         KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1102                                 ("hosed pcm channel setup"));
1103
1104                         sbuf_printf(s, "\n\t");
1105
1106                         /* it would be better to indent child channels */
1107                         sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1108                         sbuf_printf(s, "spd %d", c->speed);
1109                         if (c->speed != sndbuf_getspd(c->bufhard))
1110                                 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1111                         sbuf_printf(s, ", fmt 0x%08x", c->format);
1112                         if (c->format != sndbuf_getfmt(c->bufhard))
1113                                 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1114                         sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1115                         if (c->pid != -1)
1116                                 sbuf_printf(s, ", pid %d", c->pid);
1117                         sbuf_printf(s, "\n\t");
1118
1119                         sbuf_printf(s, "interrupts %d, ", c->interrupts);
1120                         if (c->direction == PCMDIR_REC)
1121                                 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1122                                         c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1123                                         sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1124                                         sndbuf_getblkcnt(c->bufhard),
1125                                         sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1126                                         sndbuf_getblkcnt(c->bufsoft));
1127                         else
1128                                 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1129                                         c->xruns, sndbuf_getready(c->bufsoft),
1130                                         sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1131                                         sndbuf_getblkcnt(c->bufhard),
1132                                         sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1133                                         sndbuf_getblkcnt(c->bufsoft));
1134                         sbuf_printf(s, "\n\t");
1135
1136                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1137                         sbuf_printf(s, " -> ");
1138                         f = c->feeder;
1139                         while (f->source != NULL)
1140                                 f = f->source;
1141                         while (f != NULL) {
1142                                 sbuf_printf(s, "%s", f->class->name);
1143                                 if (f->desc->type == FEEDER_FMT)
1144                                         sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1145                                 if (f->desc->type == FEEDER_RATE)
1146                                         sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1147                                 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1148                                                 f->desc->type == FEEDER_VOLUME)
1149                                         sbuf_printf(s, "(0x%08x)", f->desc->out);
1150                                 sbuf_printf(s, " -> ");
1151                                 f = f->parent;
1152                         }
1153                         sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1154                 }
1155         } else
1156                 sbuf_printf(s, " (mixer only)");
1157         snd_mtxunlock(d->lock);
1158
1159         return 0;
1160 }
1161
1162 /************************************************************************/
1163
1164 #ifdef SND_DYNSYSCTL
1165 int
1166 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1167 {
1168         struct snddev_info *d;
1169         int err, newcnt;
1170
1171         d = oidp->oid_arg1;
1172
1173         newcnt = d->vchancount;
1174         err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1175
1176         if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1177                 err = pcm_setvchans(d, newcnt);
1178
1179         return err;
1180 }
1181 #endif
1182
1183 /************************************************************************/
1184
1185 static int
1186 sound_modevent(module_t mod, int type, void *data)
1187 {
1188 #if 0
1189         return (midi_modevent(mod, type, data));
1190 #else
1191         return 0;
1192 #endif
1193 }
1194
1195 DEV_MODULE(sound, sound_modevent, NULL);
1196 MODULE_VERSION(sound, SOUND_MODVER);