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