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