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