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