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