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