]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/sound/pcm/sound.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / sound / pcm / sound.c
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * (C) 1997 Luigi Rizzo
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
28 #include <dev/sound/pcm/sound.h>
29 #include <dev/sound/pcm/ac97.h>
30 #include <dev/sound/pcm/vchan.h>
31 #include <dev/sound/pcm/dsp.h>
32 #include <dev/sound/version.h>
33 #include <sys/limits.h>
34 #include <sys/sysctl.h>
35
36 #include "feeder_if.h"
37
38 SND_DECLARE_FILE("$FreeBSD$");
39
40 devclass_t pcm_devclass;
41
42 int pcm_veto_load = 1;
43
44 #ifdef USING_DEVFS
45 int snd_unit = -1;
46 TUNABLE_INT("hw.snd.default_unit", &snd_unit);
47 #endif
48
49 static int snd_unit_auto = 0;
50 TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto);
51 SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW,
52     &snd_unit_auto, 0, "assign default unit to a newly attached device");
53
54 int snd_maxautovchans = 16;
55 /* XXX: a tunable implies that we may need more than one sound channel before
56    the system can change a sysctl (/etc/sysctl.conf), do we really need
57    this? */
58 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
59
60 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
61
62 /*
63  * XXX I've had enough with people not telling proper version/arch
64  *     while reporting problems, not after 387397913213th questions/requests.
65  */
66 static const char snd_driver_version[] =
67     __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
68 SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
69     0, "Driver version/arch");
70
71 /**
72  * @brief Unit number allocator for syncgroup IDs
73  */
74 struct unrhdr *pcmsg_unrhdr = NULL;
75
76 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
77
78 void *
79 snd_mtxcreate(const char *desc, const char *type)
80 {
81 #ifdef USING_MUTEX
82         struct mtx *m;
83
84         m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
85         mtx_init(m, desc, type, MTX_DEF);
86         return m;
87 #else
88         return (void *)0xcafebabe;
89 #endif
90 }
91
92 void
93 snd_mtxfree(void *m)
94 {
95 #ifdef USING_MUTEX
96         struct mtx *mtx = m;
97
98         /* mtx_assert(mtx, MA_OWNED); */
99         mtx_destroy(mtx);
100         free(mtx, M_DEVBUF);
101 #endif
102 }
103
104 void
105 snd_mtxassert(void *m)
106 {
107 #ifdef USING_MUTEX
108 #ifdef INVARIANTS
109         struct mtx *mtx = m;
110
111         mtx_assert(mtx, MA_OWNED);
112 #endif
113 #endif
114 }
115 /*
116 void
117 snd_mtxlock(void *m)
118 {
119 #ifdef USING_MUTEX
120         struct mtx *mtx = m;
121
122         mtx_lock(mtx);
123 #endif
124 }
125
126 void
127 snd_mtxunlock(void *m)
128 {
129 #ifdef USING_MUTEX
130         struct mtx *mtx = m;
131
132         mtx_unlock(mtx);
133 #endif
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         struct snddev_info *d;
140 #ifdef USING_MUTEX
141         flags &= INTR_MPSAFE;
142         flags |= INTR_TYPE_AV;
143 #else
144         flags = INTR_TYPE_AV;
145 #endif
146         d = device_get_softc(dev);
147         if (d != NULL && (flags & INTR_MPSAFE))
148                 d->flags |= SD_F_MPSAFE;
149
150         return bus_setup_intr(dev, res, flags,
151 #if __FreeBSD_version >= 700031
152                         NULL,
153 #endif
154                         hand, param, cookiep);
155 }
156
157 #ifndef PCM_DEBUG_MTX
158 void
159 pcm_lock(struct snddev_info *d)
160 {
161         snd_mtxlock(d->lock);
162 }
163
164 void
165 pcm_unlock(struct snddev_info *d)
166 {
167         snd_mtxunlock(d->lock);
168 }
169 #endif
170
171 struct pcm_channel *
172 pcm_getfakechan(struct snddev_info *d)
173 {
174         return d->fakechan;
175 }
176
177 static void
178 pcm_clonereset(struct snddev_info *d)
179 {
180         int cmax;
181
182         PCM_BUSYASSERT(d);
183
184         cmax = d->playcount + d->reccount - 1;
185         if (d->pvchancount > 0)
186                 cmax += MAX(d->pvchancount, snd_maxautovchans) - 1;
187         if (d->rvchancount > 0)
188                 cmax += MAX(d->rvchancount, snd_maxautovchans) - 1;
189         if (cmax > PCMMAXCLONE)
190                 cmax = PCMMAXCLONE;
191         (void)snd_clone_gc(d->clones);
192         (void)snd_clone_setmaxunit(d->clones, cmax);
193 }
194
195 static int
196 pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
197 {
198         struct pcm_channel *c, *ch, *nch;
199         int err, vcnt;
200
201         PCM_BUSYASSERT(d);
202
203         if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
204             (direction == PCMDIR_REC && d->reccount < 1))
205                 return (ENODEV);
206
207         if (!(d->flags & SD_F_AUTOVCHAN))
208                 return (EINVAL);
209
210         if (newcnt < 0 || newcnt > SND_MAXVCHANS)
211                 return (E2BIG);
212
213         if (direction == PCMDIR_PLAY)
214                 vcnt = d->pvchancount;
215         else if (direction == PCMDIR_REC)
216                 vcnt = d->rvchancount;
217         else
218                 return (EINVAL);
219
220         if (newcnt > vcnt) {
221                 KASSERT(num == -1 ||
222                     (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
223                     ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
224                     num, newcnt, vcnt));
225                 /* add new vchans - find a parent channel first */
226                 ch = NULL;
227                 CHN_FOREACH(c, d, channels.pcm) {
228                         CHN_LOCK(c);
229                         if (c->direction == direction &&
230                             ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
231                             !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
232                                 ch = c;
233                                 break;
234                         }
235                         CHN_UNLOCK(c);
236                 }
237                 if (ch == NULL)
238                         return (EBUSY);
239                 ch->flags |= CHN_F_BUSY;
240                 err = 0;
241                 while (err == 0 && newcnt > vcnt) {
242                         err = vchan_create(ch, num);
243                         if (err == 0)
244                                 vcnt++;
245                         else if (err == E2BIG && newcnt > vcnt)
246                                 device_printf(d->dev,
247                                     "%s: err=%d Maximum channel reached.\n",
248                                     __func__, err);
249                 }
250                 if (vcnt == 0)
251                         ch->flags &= ~CHN_F_BUSY;
252                 CHN_UNLOCK(ch);
253                 if (err != 0)
254                         return (err);
255                 else
256                         pcm_clonereset(d);
257         } else if (newcnt < vcnt) {
258                 KASSERT(num == -1,
259                     ("bogus vchan_destroy() request num=%d", num));
260                 CHN_FOREACH(c, d, channels.pcm) {
261                         CHN_LOCK(c);
262                         if (c->direction != direction ||
263                             CHN_EMPTY(c, children) ||
264                             !(c->flags & CHN_F_HAS_VCHAN)) {
265                                 CHN_UNLOCK(c);
266                                 continue;
267                         }
268                         CHN_FOREACH_SAFE(ch, c, nch, children) {
269                                 CHN_LOCK(ch);
270                                 if (!(ch->flags & CHN_F_BUSY)) {
271                                         CHN_UNLOCK(ch);
272                                         CHN_UNLOCK(c);
273                                         err = vchan_destroy(ch);
274                                         CHN_LOCK(c);
275                                         if (err == 0)
276                                                 vcnt--;
277                                 } else
278                                         CHN_UNLOCK(ch);
279                                 if (vcnt == newcnt)
280                                         break;
281                         }
282                         CHN_UNLOCK(c);
283                         break;
284                 }
285                 pcm_clonereset(d);
286         }
287
288         return (0);
289 }
290
291 /* return error status and a locked channel */
292 int
293 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
294     pid_t pid, int devunit)
295 {
296         struct pcm_channel *c;
297         int err, vchancount;
298
299         KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
300             !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
301             (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
302             ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
303             __func__, d, ch, direction, pid, devunit));
304         PCM_BUSYASSERT(d);
305
306         /* Double check again. */
307         if (devunit != -1) {
308                 switch (snd_unit2d(devunit)) {
309                 case SND_DEV_DSPHW_PLAY:
310                 case SND_DEV_DSPHW_VPLAY:
311                         if (direction != PCMDIR_PLAY)
312                                 return (EOPNOTSUPP);
313                         break;
314                 case SND_DEV_DSPHW_REC:
315                 case SND_DEV_DSPHW_VREC:
316                         if (direction != PCMDIR_REC)
317                                 return (EOPNOTSUPP);
318                         break;
319                 default:
320                         if (!(direction == PCMDIR_PLAY ||
321                             direction == PCMDIR_REC))
322                                 return (EOPNOTSUPP);
323                         break;
324                 }
325         }
326
327 retry_chnalloc:
328         err = EOPNOTSUPP;
329         /* scan for a free channel */
330         CHN_FOREACH(c, d, channels.pcm) {
331                 CHN_LOCK(c);
332                 if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
333                     (devunit == -1 || devunit == -2 || c->unit == devunit)) {
334                         c->flags |= CHN_F_BUSY;
335                         c->pid = pid;
336                         *ch = c;
337                         return (0);
338                 } else if (c->unit == devunit) {
339                         if (c->direction != direction)
340                                 err = EOPNOTSUPP;
341                         else if (c->flags & CHN_F_BUSY)
342                                 err = EBUSY;
343                         else
344                                 err = EINVAL;
345                         CHN_UNLOCK(c);
346                         return (err);
347                 } else if ((devunit == -1 || devunit == -2) &&
348                     c->direction == direction && (c->flags & CHN_F_BUSY))
349                         err = EBUSY;
350                 CHN_UNLOCK(c);
351         }
352
353         if (devunit == -2)
354                 return (err);
355
356         /* no channel available */
357         if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
358             snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
359                 if (direction == PCMDIR_PLAY)
360                         vchancount = d->pvchancount;
361                 else
362                         vchancount = d->rvchancount;
363                 if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
364                     (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
365                         return (err);
366                 err = pcm_setvchans(d, direction, vchancount + 1,
367                     (devunit == -1) ? -1 : snd_unit2c(devunit));
368                 if (err == 0) {
369                         if (devunit == -1)
370                                 devunit = -2;
371                         goto retry_chnalloc;
372                 }
373         }
374
375         return (err);
376 }
377
378 /* release a locked channel and unlock it */
379 int
380 pcm_chnrelease(struct pcm_channel *c)
381 {
382         PCM_BUSYASSERT(c->parentsnddev);
383         CHN_LOCKASSERT(c);
384
385         c->flags &= ~CHN_F_BUSY;
386         c->pid = -1;
387         CHN_UNLOCK(c);
388
389         return (0);
390 }
391
392 int
393 pcm_chnref(struct pcm_channel *c, int ref)
394 {
395         PCM_BUSYASSERT(c->parentsnddev);
396         CHN_LOCKASSERT(c);
397
398         c->refcount += ref;
399
400         return (c->refcount);
401 }
402
403 int
404 pcm_inprog(struct snddev_info *d, int delta)
405 {
406         snd_mtxassert(d->lock);
407
408         d->inprog += delta;
409
410         return (d->inprog);
411 }
412
413 static void
414 pcm_setmaxautovchans(struct snddev_info *d, int num)
415 {
416         PCM_BUSYASSERT(d);
417
418         if (num < 0)
419                 return;
420
421         if (num >= 0 && d->pvchancount > num)
422                 (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
423         else if (num > 0 && d->pvchancount == 0)
424                 (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
425
426         if (num >= 0 && d->rvchancount > num)
427                 (void)pcm_setvchans(d, PCMDIR_REC, num, -1);
428         else if (num > 0 && d->rvchancount == 0)
429                 (void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
430
431         pcm_clonereset(d);
432 }
433
434 #ifdef USING_DEVFS
435 static int
436 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
437 {
438         struct snddev_info *d;
439         int error, unit;
440
441         unit = snd_unit;
442         error = sysctl_handle_int(oidp, &unit, 0, req);
443         if (error == 0 && req->newptr != NULL) {
444                 d = devclass_get_softc(pcm_devclass, unit);
445                 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
446                         return EINVAL;
447                 snd_unit = unit;
448         }
449         return (error);
450 }
451 /* XXX: do we need a way to let the user change the default unit? */
452 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
453             0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
454 #endif
455
456 static int
457 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
458 {
459         struct snddev_info *d;
460         int i, v, error;
461
462         v = snd_maxautovchans;
463         error = sysctl_handle_int(oidp, &v, 0, req);
464         if (error == 0 && req->newptr != NULL) {
465                 if (v < 0)
466                         v = 0;
467                 if (v > SND_MAXVCHANS)
468                         v = SND_MAXVCHANS;
469                 snd_maxautovchans = v;
470                 for (i = 0; pcm_devclass != NULL &&
471                     i < devclass_get_maxunit(pcm_devclass); i++) {
472                         d = devclass_get_softc(pcm_devclass, i);
473                         if (!PCM_REGISTERED(d))
474                                 continue;
475                         PCM_ACQUIRE_QUICK(d);
476                         pcm_setmaxautovchans(d, v);
477                         PCM_RELEASE_QUICK(d);
478                 }
479         }
480         return (error);
481 }
482 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
483             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
484
485 struct pcm_channel *
486 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
487 {
488         struct pcm_channel *ch;
489         int direction, err, rpnum, *pnum, max;
490         int udc, device, chan;
491         char *dirs, *devname, buf[CHN_NAMELEN];
492
493         PCM_BUSYASSERT(d);
494         snd_mtxassert(d->lock);
495         KASSERT(num >= -1, ("invalid num=%d", num));
496
497
498         switch (dir) {
499         case PCMDIR_PLAY:
500                 dirs = "play";
501                 direction = PCMDIR_PLAY;
502                 pnum = &d->playcount;
503                 device = SND_DEV_DSPHW_PLAY;
504                 max = SND_MAXHWCHAN;
505                 break;
506         case PCMDIR_PLAY_VIRTUAL:
507                 dirs = "virtual";
508                 direction = PCMDIR_PLAY;
509                 pnum = &d->pvchancount;
510                 device = SND_DEV_DSPHW_VPLAY;
511                 max = SND_MAXVCHANS;
512                 break;
513         case PCMDIR_REC:
514                 dirs = "record";
515                 direction = PCMDIR_REC;
516                 pnum = &d->reccount;
517                 device = SND_DEV_DSPHW_REC;
518                 max = SND_MAXHWCHAN;
519                 break;
520         case PCMDIR_REC_VIRTUAL:
521                 dirs = "virtual";
522                 direction = PCMDIR_REC;
523                 pnum = &d->rvchancount;
524                 device = SND_DEV_DSPHW_VREC;
525                 max = SND_MAXVCHANS;
526                 break;
527         default:
528                 return (NULL);
529         }
530
531         chan = (num == -1) ? 0 : num;
532
533         if (*pnum >= max || chan >= max)
534                 return (NULL);
535
536         rpnum = 0;
537
538         CHN_FOREACH(ch, d, channels.pcm) {
539                 if (CHN_DEV(ch) != device)
540                         continue;
541                 if (chan == CHN_CHAN(ch)) {
542                         if (num != -1) {
543                                 device_printf(d->dev,
544                                     "channel num=%d allocated!\n", chan);
545                                 return (NULL);
546                         }
547                         chan++;
548                         if (chan >= max) {
549                                 device_printf(d->dev,
550                                     "chan=%d > %d\n", chan, max);
551                                 return (NULL);
552                         }
553                 }
554                 rpnum++;
555         }
556
557         if (*pnum != rpnum) {
558                 device_printf(d->dev,
559                     "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
560                     __func__, dirs, *pnum, rpnum);
561                 return (NULL);
562         }
563
564         udc = snd_mkunit(device_get_unit(d->dev), device, chan);
565         devname = dsp_unit2name(buf, sizeof(buf), udc);
566
567         if (devname == NULL) {
568                 device_printf(d->dev,
569                     "Failed to query device name udc=0x%08x\n", udc);
570                 return (NULL);
571         }
572
573         pcm_unlock(d);
574         ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
575         ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
576         ch->unit = udc;
577         ch->pid = -1;
578         ch->parentsnddev = d;
579         ch->parentchannel = parent;
580         ch->dev = d->dev;
581         ch->trigger = PCMTRIG_STOP;
582         snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
583             device_get_nameunit(ch->dev), dirs, devname);
584
585         err = chn_init(ch, devinfo, dir, direction);
586         pcm_lock(d);
587         if (err) {
588                 device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
589                     ch->name, err);
590                 kobj_delete(ch->methods, M_DEVBUF);
591                 free(ch, M_DEVBUF);
592                 return (NULL);
593         }
594
595         return (ch);
596 }
597
598 int
599 pcm_chn_destroy(struct pcm_channel *ch)
600 {
601         struct snddev_info *d;
602         int err;
603
604         d = ch->parentsnddev;
605         PCM_BUSYASSERT(d);
606
607         err = chn_kill(ch);
608         if (err) {
609                 device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
610                     ch->name, err);
611                 return (err);
612         }
613
614         kobj_delete(ch->methods, M_DEVBUF);
615         free(ch, M_DEVBUF);
616
617         return (0);
618 }
619
620 int
621 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
622 {
623         struct pcm_channel *tmp, *after;
624         int num;
625
626         PCM_BUSYASSERT(d);
627         snd_mtxassert(d->lock);
628         KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
629             ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
630
631         after = NULL;
632         tmp = NULL;
633         num = 0;
634
635         /*
636          * Look for possible device collision.
637          */
638         CHN_FOREACH(tmp, d, channels.pcm) {
639                 if (tmp->unit == ch->unit) {
640                         device_printf(d->dev, "%s(): Device collision "
641                             "old=%p new=%p devunit=0x%08x\n",
642                             __func__, tmp, ch, ch->unit);
643                         return (ENODEV);
644                 }
645                 if (CHN_DEV(tmp) < CHN_DEV(ch)) {
646                         if (num == 0)
647                                 after = tmp;
648                         continue;
649                 } else if (CHN_DEV(tmp) > CHN_DEV(ch))
650                         break;
651                 num++;
652                 if (CHN_CHAN(tmp) < CHN_CHAN(ch))
653                         after = tmp;
654                 else if (CHN_CHAN(tmp) > CHN_CHAN(ch))
655                         break;
656         }
657
658         if (after != NULL) {
659                 CHN_INSERT_AFTER(after, ch, channels.pcm);
660         } else {
661                 CHN_INSERT_HEAD(d, ch, channels.pcm);
662         }
663
664         switch (CHN_DEV(ch)) {
665         case SND_DEV_DSPHW_PLAY:
666                 d->playcount++;
667                 break;
668         case SND_DEV_DSPHW_VPLAY:
669                 d->pvchancount++;
670                 break;
671         case SND_DEV_DSPHW_REC:
672                 d->reccount++;
673                 break;
674         case SND_DEV_DSPHW_VREC:
675                 d->rvchancount++;
676                 break;
677         default:
678                 break;
679         }
680
681         d->devcount++;
682
683         return (0);
684 }
685
686 int
687 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
688 {
689         struct pcm_channel *tmp;
690
691         PCM_BUSYASSERT(d);
692         snd_mtxassert(d->lock);
693
694         tmp = NULL;
695
696         CHN_FOREACH(tmp, d, channels.pcm) {
697                 if (tmp == ch)
698                         break;
699         }
700
701         if (tmp != ch)
702                 return (EINVAL);
703
704         CHN_REMOVE(d, ch, channels.pcm);
705
706         switch (CHN_DEV(ch)) {
707         case SND_DEV_DSPHW_PLAY:
708                 d->playcount--;
709                 break;
710         case SND_DEV_DSPHW_VPLAY:
711                 d->pvchancount--;
712                 break;
713         case SND_DEV_DSPHW_REC:
714                 d->reccount--;
715                 break;
716         case SND_DEV_DSPHW_VREC:
717                 d->rvchancount--;
718                 break;
719         default:
720                 break;
721         }
722
723         d->devcount--;
724
725         return (0);
726 }
727
728 int
729 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
730 {
731         struct snddev_info *d = device_get_softc(dev);
732         struct pcm_channel *ch;
733         int err;
734
735         PCM_BUSYASSERT(d);
736
737         pcm_lock(d);
738         ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
739         if (!ch) {
740                 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
741                     cls->name, dir, devinfo);
742                 pcm_unlock(d);
743                 return (ENODEV);
744         }
745
746         err = pcm_chn_add(d, ch);
747         pcm_unlock(d);
748         if (err) {
749                 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
750                     ch->name, err);
751                 pcm_chn_destroy(ch);
752         }
753
754         return (err);
755 }
756
757 static int
758 pcm_killchan(device_t dev)
759 {
760         struct snddev_info *d = device_get_softc(dev);
761         struct pcm_channel *ch;
762         int error;
763
764         PCM_BUSYASSERT(d);
765
766         ch = CHN_FIRST(d, channels.pcm);
767
768         pcm_lock(d);
769         error = pcm_chn_remove(d, ch);
770         pcm_unlock(d);
771         if (error)
772                 return (error);
773         return (pcm_chn_destroy(ch));
774 }
775
776 int
777 pcm_setstatus(device_t dev, char *str)
778 {
779         struct snddev_info *d = device_get_softc(dev);
780
781         PCM_BUSYASSERT(d);
782
783         if (d->playcount == 0 || d->reccount == 0)
784                 d->flags |= SD_F_SIMPLEX;
785
786         if ((d->playcount > 0 || d->reccount > 0) &&
787             !(d->flags & SD_F_AUTOVCHAN)) {
788                 d->flags |= SD_F_AUTOVCHAN;
789                 vchan_initsys(dev);
790         }
791
792         pcm_setmaxautovchans(d, snd_maxautovchans);
793
794         strlcpy(d->status, str, SND_STATUSLEN);
795
796         pcm_lock(d);
797
798         /* Last stage, enable cloning. */
799         if (d->clones != NULL)
800                 (void)snd_clone_enable(d->clones);
801
802         /* Done, we're ready.. */
803         d->flags |= SD_F_REGISTERED;
804
805         PCM_RELEASE(d);
806
807         pcm_unlock(d);
808
809         if (snd_unit < 0 || snd_unit_auto != 0)
810                 snd_unit = device_get_unit(dev);
811
812         return (0);
813 }
814
815 uint32_t
816 pcm_getflags(device_t dev)
817 {
818         struct snddev_info *d = device_get_softc(dev);
819
820         return d->flags;
821 }
822
823 void
824 pcm_setflags(device_t dev, uint32_t val)
825 {
826         struct snddev_info *d = device_get_softc(dev);
827
828         d->flags = val;
829 }
830
831 void *
832 pcm_getdevinfo(device_t dev)
833 {
834         struct snddev_info *d = device_get_softc(dev);
835
836         return d->devinfo;
837 }
838
839 unsigned int
840 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
841 {
842         struct snddev_info *d = device_get_softc(dev);
843         int sz, x;
844
845         sz = 0;
846         if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
847                 x = sz;
848                 RANGE(sz, minbufsz, maxbufsz);
849                 if (x != sz)
850                         device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
851                 x = minbufsz;
852                 while (x < sz)
853                         x <<= 1;
854                 if (x > sz)
855                         x >>= 1;
856                 if (x != sz) {
857                         device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
858                         sz = x;
859                 }
860         } else {
861                 sz = deflt;
862         }
863
864         d->bufsz = sz;
865
866         return sz;
867 }
868
869 #if defined(SND_DYNSYSCTL) && defined(SND_DEBUG)
870 static int
871 sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
872 {
873         struct snddev_info *d;
874         uint32_t flags;
875         int err;
876
877         d = oidp->oid_arg1;
878         if (!PCM_REGISTERED(d) || d->clones == NULL)
879                 return (ENODEV);
880
881         PCM_ACQUIRE_QUICK(d);
882
883         flags = snd_clone_getflags(d->clones);
884         err = sysctl_handle_int(oidp, &flags, 0, req);
885
886         if (err == 0 && req->newptr != NULL) {
887                 if (flags & ~SND_CLONE_MASK)
888                         err = EINVAL;
889                 else
890                         (void)snd_clone_setflags(d->clones, flags);
891         }
892
893         PCM_RELEASE_QUICK(d);
894
895         return (err);
896 }
897
898 static int
899 sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
900 {
901         struct snddev_info *d;
902         int err, deadline;
903
904         d = oidp->oid_arg1;
905         if (!PCM_REGISTERED(d) || d->clones == NULL)
906                 return (ENODEV);
907
908         PCM_ACQUIRE_QUICK(d);
909
910         deadline = snd_clone_getdeadline(d->clones);
911         err = sysctl_handle_int(oidp, &deadline, 0, req);
912
913         if (err == 0 && req->newptr != NULL) {
914                 if (deadline < 0)
915                         err = EINVAL;
916                 else
917                         (void)snd_clone_setdeadline(d->clones, deadline);
918         }
919
920         PCM_RELEASE_QUICK(d);
921
922         return (err);
923 }
924
925 static int
926 sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
927 {
928         struct snddev_info *d;
929         int err, val;
930
931         d = oidp->oid_arg1;
932         if (!PCM_REGISTERED(d) || d->clones == NULL)
933                 return (ENODEV);
934
935         val = 0;
936         err = sysctl_handle_int(oidp, &val, 0, req);
937
938         if (err == 0 && req->newptr != NULL && val != 0) {
939                 PCM_ACQUIRE_QUICK(d);
940                 val = snd_clone_gc(d->clones);
941                 PCM_RELEASE_QUICK(d);
942                 if (bootverbose != 0 || snd_verbose > 3)
943                         device_printf(d->dev, "clone gc: pruned=%d\n", val);
944         }
945
946         return (err);
947 }
948
949 static int
950 sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
951 {
952         struct snddev_info *d;
953         int i, err, val;
954
955         val = 0;
956         err = sysctl_handle_int(oidp, &val, 0, req);
957
958         if (err == 0 && req->newptr != NULL && val != 0) {
959                 for (i = 0; pcm_devclass != NULL &&
960                     i < devclass_get_maxunit(pcm_devclass); i++) {
961                         d = devclass_get_softc(pcm_devclass, i);
962                         if (!PCM_REGISTERED(d) || d->clones == NULL)
963                                 continue;
964                         PCM_ACQUIRE_QUICK(d);
965                         val = snd_clone_gc(d->clones);
966                         PCM_RELEASE_QUICK(d);
967                         if (bootverbose != 0 || snd_verbose > 3)
968                                 device_printf(d->dev, "clone gc: pruned=%d\n",
969                                     val);
970                 }
971         }
972
973         return (err);
974 }
975 SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
976     0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
977     "global clone garbage collector");
978 #endif
979
980 int
981 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
982 {
983         struct snddev_info *d;
984
985         if (pcm_veto_load) {
986                 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
987
988                 return EINVAL;
989         }
990
991         if (device_get_unit(dev) > PCMMAXUNIT) {
992                 device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
993                     device_get_unit(dev), PCMMAXUNIT);
994                 device_printf(dev,
995                     "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
996                 return ENODEV;
997         }
998
999         d = device_get_softc(dev);
1000         d->dev = dev;
1001         d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
1002         cv_init(&d->cv, device_get_nameunit(dev));
1003         PCM_ACQUIRE_QUICK(d);
1004         dsp_cdevinfo_init(d);
1005 #if 0
1006         /*
1007          * d->flags should be cleared by the allocator of the softc.
1008          * We cannot clear this field here because several devices set
1009          * this flag before calling pcm_register().
1010          */
1011         d->flags = 0;
1012 #endif
1013         d->devinfo = devinfo;
1014         d->devcount = 0;
1015         d->reccount = 0;
1016         d->playcount = 0;
1017         d->pvchancount = 0;
1018         d->rvchancount = 0;
1019         d->pvchanrate = 0;
1020         d->pvchanformat = 0;
1021         d->rvchanrate = 0;
1022         d->rvchanformat = 0;
1023         d->inprog = 0;
1024
1025         /*
1026          * Create clone manager, disabled by default. Cloning will be
1027          * enabled during final stage of driver iniialization through
1028          * pcm_setstatus().
1029          */
1030         d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
1031             SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
1032             SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
1033             SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
1034
1035         if (bootverbose != 0 || snd_verbose > 3) {
1036                 device_printf(dev,
1037                     "clone manager: deadline=%dms flags=0x%08x\n",
1038                     snd_clone_getdeadline(d->clones),
1039                     snd_clone_getflags(d->clones));
1040         }
1041
1042         CHN_INIT(d, channels.pcm);
1043         CHN_INIT(d, channels.pcm.busy);
1044
1045         /* XXX This is incorrect, but lets play along for now. */
1046         if ((numplay == 0 || numrec == 0) && numplay != numrec)
1047                 d->flags |= SD_F_SIMPLEX;
1048
1049         d->fakechan = fkchan_setup(dev);
1050         chn_init(d->fakechan, NULL, 0, 0);
1051
1052 #ifdef SND_DYNSYSCTL
1053         sysctl_ctx_init(&d->play_sysctl_ctx);
1054         d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
1055             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
1056             CTLFLAG_RD, 0, "playback channels node");
1057         sysctl_ctx_init(&d->rec_sysctl_ctx);
1058         d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
1059             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
1060             CTLFLAG_RD, 0, "record channels node");
1061         /* XXX: an user should be able to set this with a control tool, the
1062            sysadmin then needs min+max sysctls for this */
1063         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
1064             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
1065             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
1066 #ifdef SND_DEBUG
1067         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1068             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1069             "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
1070             sysctl_dev_pcm_clone_flags, "IU",
1071             "clone flags");
1072         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1073             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1074             "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1075             sysctl_dev_pcm_clone_deadline, "I",
1076             "clone expiration deadline (ms)");
1077         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1078             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1079             "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1080             sysctl_dev_pcm_clone_gc, "I",
1081             "clone garbage collector");
1082 #endif
1083 #endif
1084
1085         if (numplay > 0 || numrec > 0) {
1086                 d->flags |= SD_F_AUTOVCHAN;
1087                 vchan_initsys(dev);
1088         }
1089
1090         sndstat_register(dev, d->status, sndstat_prepare_pcm);
1091
1092         return 0;
1093 }
1094
1095 int
1096 pcm_unregister(device_t dev)
1097 {
1098         struct snddev_info *d;
1099         struct pcm_channel *ch;
1100         struct thread *td;
1101         int i;
1102
1103         td = curthread;
1104         d = device_get_softc(dev);
1105
1106         if (!PCM_ALIVE(d)) {
1107                 device_printf(dev, "unregister: device not configured\n");
1108                 return (0);
1109         }
1110
1111         if (sndstat_acquire(td) != 0) {
1112                 device_printf(dev, "unregister: sndstat busy\n");
1113                 return (EBUSY);
1114         }
1115
1116         pcm_lock(d);
1117         PCM_WAIT(d);
1118
1119         if (d->inprog != 0) {
1120                 device_printf(dev, "unregister: operation in progress\n");
1121                 pcm_unlock(d);
1122                 sndstat_release(td);
1123                 return (EBUSY);
1124         }
1125
1126         PCM_ACQUIRE(d);
1127         pcm_unlock(d);
1128
1129         CHN_FOREACH(ch, d, channels.pcm) {
1130                 CHN_LOCK(ch);
1131                 if (ch->refcount > 0) {
1132                         device_printf(dev,
1133                             "unregister: channel %s busy (pid %d)\n",
1134                             ch->name, ch->pid);
1135                         CHN_UNLOCK(ch);
1136                         PCM_RELEASE_QUICK(d);
1137                         sndstat_release(td);
1138                         return (EBUSY);
1139                 }
1140                 CHN_UNLOCK(ch);
1141         }
1142
1143         if (d->clones != NULL) {
1144                 if (snd_clone_busy(d->clones) != 0) {
1145                         device_printf(dev, "unregister: clone busy\n");
1146                         PCM_RELEASE_QUICK(d);
1147                         sndstat_release(td);
1148                         return (EBUSY);
1149                 } else {
1150                         pcm_lock(d);
1151                         (void)snd_clone_disable(d->clones);
1152                         pcm_unlock(d);
1153                 }
1154         }
1155
1156         if (mixer_uninit(dev) == EBUSY) {
1157                 device_printf(dev, "unregister: mixer busy\n");
1158                 pcm_lock(d);
1159                 if (d->clones != NULL)
1160                         (void)snd_clone_enable(d->clones);
1161                 PCM_RELEASE(d);
1162                 pcm_unlock(d);
1163                 sndstat_release(td);
1164                 return (EBUSY);
1165         }
1166
1167         pcm_lock(d);
1168         d->flags |= SD_F_DYING;
1169         d->flags &= ~SD_F_REGISTERED;
1170         pcm_unlock(d);
1171
1172         /*
1173          * No lock being held, so this thing can be flushed without
1174          * stucking into devdrn oblivion.
1175          */
1176         if (d->clones != NULL) {
1177                 snd_clone_destroy(d->clones);
1178                 d->clones = NULL;
1179         }
1180
1181 #ifdef SND_DYNSYSCTL
1182         if (d->play_sysctl_tree != NULL) {
1183                 sysctl_ctx_free(&d->play_sysctl_ctx);
1184                 d->play_sysctl_tree = NULL;
1185         }
1186         if (d->rec_sysctl_tree != NULL) {
1187                 sysctl_ctx_free(&d->rec_sysctl_ctx);
1188                 d->rec_sysctl_tree = NULL;
1189         }
1190 #endif
1191
1192         while (!CHN_EMPTY(d, channels.pcm))
1193                 pcm_killchan(dev);
1194
1195         chn_kill(d->fakechan);
1196         fkchan_kill(d->fakechan);
1197
1198         dsp_cdevinfo_flush(d);
1199
1200         pcm_lock(d);
1201         PCM_RELEASE(d);
1202         cv_destroy(&d->cv);
1203         pcm_unlock(d);
1204         snd_mtxfree(d->lock);
1205         sndstat_unregister(dev);
1206         sndstat_release(td);
1207
1208         if (snd_unit == device_get_unit(dev)) {
1209                 /*
1210                  * Reassign default unit to the next available dev, but
1211                  * first, reset snd_unit to something ridiculous.
1212                  */
1213                 snd_unit = -1;
1214                 for (i = 0; pcm_devclass != NULL &&
1215                     i < devclass_get_maxunit(pcm_devclass); i++) {
1216                         if (device_get_unit(dev) == i)
1217                                 continue;
1218                         d = devclass_get_softc(pcm_devclass, i);
1219                         if (PCM_REGISTERED(d)) {
1220                                 snd_unit = i;
1221                                 break;
1222                         }
1223                 }
1224         }
1225
1226         return (0);
1227 }
1228
1229 /************************************************************************/
1230
1231 static int
1232 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1233 {
1234         struct snddev_info *d;
1235         struct pcm_channel *c;
1236         struct pcm_feeder *f;
1237
1238         if (verbose < 1)
1239                 return 0;
1240
1241         d = device_get_softc(dev);
1242         if (!d)
1243                 return ENXIO;
1244
1245         PCM_BUSYASSERT(d);
1246
1247         if (CHN_EMPTY(d, channels.pcm)) {
1248                 sbuf_printf(s, " (mixer only)");
1249                 return 0;
1250         }
1251
1252         sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
1253                         d->playcount, d->pvchancount,
1254                         d->reccount, d->rvchancount,
1255                         (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1256 #ifdef USING_DEVFS
1257                         (device_get_unit(dev) == snd_unit)? " default" : ""
1258 #else
1259                         ""
1260 #endif
1261                         );
1262
1263         if (verbose <= 1)
1264                 return 0;
1265
1266         CHN_FOREACH(c, d, channels.pcm) {
1267
1268                 KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1269                         ("hosed pcm channel setup"));
1270
1271                 sbuf_printf(s, "\n\t");
1272
1273                 /* it would be better to indent child channels */
1274                 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1275                 sbuf_printf(s, "spd %d", c->speed);
1276                 if (c->speed != sndbuf_getspd(c->bufhard))
1277                         sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1278                 sbuf_printf(s, ", fmt 0x%08x", c->format);
1279                 if (c->format != sndbuf_getfmt(c->bufhard))
1280                         sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1281                 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1282                 if (c->pid != -1)
1283                         sbuf_printf(s, ", pid %d", c->pid);
1284                 sbuf_printf(s, "\n\t");
1285
1286                 sbuf_printf(s, "interrupts %d, ", c->interrupts);
1287                 if (c->direction == PCMDIR_REC)
1288                         sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1289                                 c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1290                                 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1291                                 sndbuf_getblkcnt(c->bufhard),
1292                                 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1293                                 sndbuf_getblkcnt(c->bufsoft));
1294                 else
1295                         sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1296                                 c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
1297                                 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1298                                 sndbuf_getblkcnt(c->bufhard),
1299                                 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1300                                 sndbuf_getblkcnt(c->bufsoft));
1301                 sbuf_printf(s, "\n\t");
1302
1303                 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1304                 sbuf_printf(s, " -> ");
1305                 f = c->feeder;
1306                 while (f->source != NULL)
1307                         f = f->source;
1308                 while (f != NULL) {
1309                         sbuf_printf(s, "%s", f->class->name);
1310                         if (f->desc->type == FEEDER_FMT)
1311                                 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1312                         if (f->desc->type == FEEDER_RATE)
1313                                 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1314                         if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1315                                         f->desc->type == FEEDER_VOLUME)
1316                                 sbuf_printf(s, "(0x%08x)", f->desc->out);
1317                         sbuf_printf(s, " -> ");
1318                         f = f->parent;
1319                 }
1320                 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1321         }
1322
1323         return 0;
1324 }
1325
1326 /************************************************************************/
1327
1328 #ifdef SND_DYNSYSCTL
1329 int
1330 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1331 {
1332         struct snddev_info *d;
1333         int direction, vchancount;
1334         int err, cnt;
1335
1336         d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
1337         if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
1338                 return (EINVAL);
1339
1340         pcm_lock(d);
1341         PCM_WAIT(d);
1342
1343         switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
1344         case VCHAN_PLAY:
1345                 direction = PCMDIR_PLAY;
1346                 vchancount = d->pvchancount;
1347                 cnt = d->playcount;
1348                 break;
1349         case VCHAN_REC:
1350                 direction = PCMDIR_REC;
1351                 vchancount = d->rvchancount;
1352                 cnt = d->reccount;
1353                 break;
1354         default:
1355                 pcm_unlock(d);
1356                 return (EINVAL);
1357                 break;
1358         }
1359
1360         if (cnt < 1) {
1361                 pcm_unlock(d);
1362                 return (ENODEV);
1363         }
1364
1365         PCM_ACQUIRE(d);
1366         pcm_unlock(d);
1367
1368         cnt = vchancount;
1369         err = sysctl_handle_int(oidp, &cnt, 0, req);
1370
1371         if (err == 0 && req->newptr != NULL && vchancount != cnt) {
1372                 if (cnt < 0)
1373                         cnt = 0;
1374                 if (cnt > SND_MAXVCHANS)
1375                         cnt = SND_MAXVCHANS;
1376                 err = pcm_setvchans(d, direction, cnt, -1);
1377         }
1378
1379         PCM_RELEASE_QUICK(d);
1380
1381         return err;
1382 }
1383 #endif
1384
1385 /************************************************************************/
1386
1387 /**
1388  * @brief       Handle OSSv4 SNDCTL_SYSINFO ioctl.
1389  *
1390  * @param si    Pointer to oss_sysinfo struct where information about the
1391  *              sound subsystem will be written/copied.
1392  *
1393  * This routine returns information about the sound system, such as the
1394  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1395  * Also includes a bitmask showing which of the above types of devices
1396  * are open (busy).
1397  *
1398  * @note
1399  * Calling threads must not hold any snddev_info or pcm_channel locks.
1400  *
1401  * @author      Ryan Beasley <ryanb@FreeBSD.org>
1402  */
1403 void
1404 sound_oss_sysinfo(oss_sysinfo *si)
1405 {
1406         static char si_product[] = "FreeBSD native OSS ABI";
1407         static char si_version[] = __XSTRING(__FreeBSD_version);
1408         static int intnbits = sizeof(int) * 8;  /* Better suited as macro?
1409                                                    Must pester a C guru. */
1410
1411         struct snddev_info *d;
1412         struct pcm_channel *c;
1413         int i, j, ncards;
1414         
1415         ncards = 0;
1416
1417         strlcpy(si->product, si_product, sizeof(si->product));
1418         strlcpy(si->version, si_version, sizeof(si->version));
1419         si->versionnum = SOUND_VERSION;
1420
1421         /*
1422          * Iterate over PCM devices and their channels, gathering up data
1423          * for the numaudios, ncards, and openedaudio fields.
1424          */
1425         si->numaudios = 0;
1426         bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1427
1428         j = 0;
1429
1430         for (i = 0; pcm_devclass != NULL &&
1431             i < devclass_get_maxunit(pcm_devclass); i++) {
1432                 d = devclass_get_softc(pcm_devclass, i);
1433                 if (!PCM_REGISTERED(d))
1434                         continue;
1435
1436                 /* XXX Need Giant magic entry ??? */
1437
1438                 /* See note in function's docblock */
1439                 mtx_assert(d->lock, MA_NOTOWNED);
1440                 pcm_lock(d);
1441
1442                 si->numaudios += d->devcount;
1443                 ++ncards;
1444
1445                 CHN_FOREACH(c, d, channels.pcm) {
1446                         mtx_assert(c->lock, MA_NOTOWNED);
1447                         CHN_LOCK(c);
1448                         if (c->flags & CHN_F_BUSY)
1449                                 si->openedaudio[j / intnbits] |=
1450                                     (1 << (j % intnbits));
1451                         CHN_UNLOCK(c);
1452                         j++;
1453                 }
1454
1455                 pcm_unlock(d);
1456         }
1457
1458         si->numsynths = 0;      /* OSSv4 docs:  this field is obsolete */
1459         /**
1460          * @todo        Collect num{midis,timers}.
1461          *
1462          * Need access to sound/midi/midi.c::midistat_lock in order
1463          * to safely touch midi_devices and get a head count of, well,
1464          * MIDI devices.  midistat_lock is a global static (i.e., local to
1465          * midi.c), but midi_devices is a regular global; should the mutex
1466          * be publicized, or is there another way to get this information?
1467          *
1468          * NB:  MIDI/sequencer stuff is currently on hold.
1469          */
1470         si->nummidis = 0;
1471         si->numtimers = 0;
1472         si->nummixers = mixer_count;
1473         si->numcards = ncards;
1474                 /* OSSv4 docs:  Intended only for test apps; API doesn't
1475                    really have much of a concept of cards.  Shouldn't be
1476                    used by applications. */
1477
1478         /**
1479          * @todo        Fill in "busy devices" fields.
1480          *
1481          *  si->openedmidi = " MIDI devices
1482          */
1483         bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1484
1485         /*
1486          * Si->filler is a reserved array, but according to docs each
1487          * element should be set to -1.
1488          */
1489         for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1490                 si->filler[i] = -1;
1491 }
1492
1493 /************************************************************************/
1494
1495 static int
1496 sound_modevent(module_t mod, int type, void *data)
1497 {
1498         int ret;
1499 #if 0
1500         return (midi_modevent(mod, type, data));
1501 #else
1502         ret = 0;
1503
1504         switch(type) {
1505                 case MOD_LOAD:
1506                         pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1507                         break;
1508                 case MOD_UNLOAD:
1509                 case MOD_SHUTDOWN:
1510                         ret = sndstat_acquire(curthread);
1511                         if (ret != 0)
1512                                 break;
1513                         if (pcmsg_unrhdr != NULL) {
1514                                 delete_unrhdr(pcmsg_unrhdr);
1515                                 pcmsg_unrhdr = NULL;
1516                         }
1517                         break;
1518                 default:
1519                         ret = EOPNOTSUPP;
1520         }
1521
1522         return ret;
1523 #endif
1524 }
1525
1526 DEV_MODULE(sound, sound_modevent, NULL);
1527 MODULE_VERSION(sound, SOUND_MODVER);