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