sound: Create the first /dev/dsp* links
[dragonfly.git] / sys / dev / sound / pcm / dsp.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>
984263bc
MD
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
2a1ad637
FT
29#ifdef HAVE_KERNEL_OPTION_HEADERS
30#include "opt_snd.h"
31#endif
984263bc
MD
32
33#include <dev/sound/pcm/sound.h>
2a1ad637 34#include <sys/ctype.h>
67931cc4
FT
35#include <sys/device.h>
36#include <sys/eventhandler.h>
2a1ad637 37#include <sys/lock.h>
2a1ad637
FT
38#include <sys/sysent.h>
39
40#include <vm/vm.h>
41#include <vm/vm_object.h>
42#include <vm/vm_page.h>
43#include <vm/vm_pager.h>
44
45SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/dsp.c 274035 2014-11-03 11:11:45Z bapt $");
46
47static int dsp_mmap_allow_prot_exec = 0;
48SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW,
49 &dsp_mmap_allow_prot_exec, 0,
50 "linux mmap compatibility (-1=force disable 0=auto 1=force enable)");
51
52struct dsp_cdevinfo {
53 struct pcm_channel *rdch, *wrch;
54 struct pcm_channel *volch;
55 int busy, simplex;
56 TAILQ_ENTRY(dsp_cdevinfo) link;
57};
984263bc 58
2a1ad637
FT
59#define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
60#define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
61#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch)
62#define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex)
63
64#define DSP_CDEVINFO_CACHESIZE 8
65
66#define DSP_REGISTERED(x, y) (PCM_REGISTERED(x) && \
67 (y) != NULL && (y)->si_drv1 != NULL)
984263bc
MD
68
69#define OLDPCM_IOCTL
70
71static d_open_t dsp_open;
72static d_close_t dsp_close;
73static d_read_t dsp_read;
74static d_write_t dsp_write;
75static d_ioctl_t dsp_ioctl;
88a31edf 76static d_kqfilter_t dsp_kqfilter;
984263bc 77static d_mmap_t dsp_mmap;
2a1ad637 78static d_mmap_single_t dsp_mmap_single;
984263bc 79
88a31edf
FT
80static void dsp_filter_detach(struct knote *);
81static int dsp_filter_read(struct knote *, long);
82static int dsp_filter_write(struct knote *, long);
83
84struct dev_ops dsp_ops = {
fef8985e
MD
85 .d_open = dsp_open,
86 .d_close = dsp_close,
87 .d_read = dsp_read,
88 .d_write = dsp_write,
89 .d_ioctl = dsp_ioctl,
88a31edf 90 .d_kqfilter = dsp_kqfilter,
fef8985e 91 .d_mmap = dsp_mmap,
2a1ad637 92 .d_mmap_single = dsp_mmap_single,
984263bc
MD
93};
94
2a1ad637
FT
95static eventhandler_tag dsp_ehtag = NULL;
96static int dsp_umax = -1;
97static int dsp_cmax = -1;
98
99static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
100static int dsp_oss_syncstart(int sg_id);
101static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
102static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled);
103static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
104static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
105static int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask);
106#ifdef OSSV4_EXPERIMENT
107static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
108static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
109static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
110static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
111static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
112#endif
113
114static struct snddev_info *
558a398b 115dsp_get_info(struct cdev *dev)
984263bc 116{
2a1ad637 117 return (devclass_get_softc(pcm_devclass, PCMUNIT(dev)));
984263bc
MD
118}
119
2a1ad637 120static uint32_t
558a398b 121dsp_get_flags(struct cdev *dev)
984263bc
MD
122{
123 device_t bdev;
984263bc 124
2a1ad637 125 bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
984263bc 126
2a1ad637 127 return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff);
984263bc
MD
128}
129
130static void
2a1ad637 131dsp_set_flags(struct cdev *dev, uint32_t flags)
984263bc
MD
132{
133 device_t bdev;
984263bc 134
2a1ad637 135 bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
984263bc 136
2a1ad637
FT
137 if (bdev != NULL)
138 pcm_setflags(bdev, flags);
984263bc
MD
139}
140
141/*
558a398b 142 * return the channels associated with an open device instance.
984263bc
MD
143 * lock channels specified.
144 */
145static int
2a1ad637
FT
146getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch,
147 uint32_t prio)
984263bc
MD
148{
149 struct snddev_info *d;
2a1ad637
FT
150 struct pcm_channel *ch;
151 uint32_t flags;
152
153 if (PCM_SIMPLEX(dev) != 0) {
154 d = dsp_get_info(dev);
155 if (!PCM_REGISTERED(d))
156 return (ENXIO);
157 PCM_LOCK(d);
158 PCM_WAIT(d);
159 PCM_ACQUIRE(d);
160 /*
161 * Note: order is important -
162 * pcm flags -> prio query flags -> wild guess
163 */
164 ch = NULL;
165 flags = dsp_get_flags(dev);
166 if (flags & SD_F_PRIO_WR) {
167 ch = PCM_RDCH(dev);
168 PCM_RDCH(dev) = NULL;
169 } else if (flags & SD_F_PRIO_RD) {
170 ch = PCM_WRCH(dev);
171 PCM_WRCH(dev) = NULL;
172 } else if (prio & SD_F_PRIO_WR) {
173 ch = PCM_RDCH(dev);
174 PCM_RDCH(dev) = NULL;
175 flags |= SD_F_PRIO_WR;
176 } else if (prio & SD_F_PRIO_RD) {
177 ch = PCM_WRCH(dev);
178 PCM_WRCH(dev) = NULL;
179 flags |= SD_F_PRIO_RD;
180 } else if (PCM_WRCH(dev) != NULL) {
181 ch = PCM_RDCH(dev);
182 PCM_RDCH(dev) = NULL;
183 flags |= SD_F_PRIO_WR;
184 } else if (PCM_RDCH(dev) != NULL) {
185 ch = PCM_WRCH(dev);
186 PCM_WRCH(dev) = NULL;
187 flags |= SD_F_PRIO_RD;
188 }
189 PCM_SIMPLEX(dev) = 0;
984263bc 190 dsp_set_flags(dev, flags);
2a1ad637
FT
191 if (ch != NULL) {
192 CHN_LOCK(ch);
193 pcm_chnref(ch, -1);
194 pcm_chnrelease(ch);
984263bc 195 }
2a1ad637
FT
196 PCM_RELEASE(d);
197 PCM_UNLOCK(d);
984263bc 198 }
984263bc 199
2a1ad637
FT
200 *rdch = PCM_RDCH(dev);
201 *wrch = PCM_WRCH(dev);
202
203 if (*rdch != NULL && (prio & SD_F_PRIO_RD))
984263bc 204 CHN_LOCK(*rdch);
2a1ad637 205 if (*wrch != NULL && (prio & SD_F_PRIO_WR))
984263bc
MD
206 CHN_LOCK(*wrch);
207
2a1ad637 208 return (0);
984263bc
MD
209}
210
211/* unlock specified channels */
212static void
2a1ad637
FT
213relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch,
214 uint32_t prio)
984263bc 215{
2a1ad637 216 if (wrch != NULL && (prio & SD_F_PRIO_WR))
984263bc 217 CHN_UNLOCK(wrch);
2a1ad637 218 if (rdch != NULL && (prio & SD_F_PRIO_RD))
984263bc 219 CHN_UNLOCK(rdch);
984263bc
MD
220}
221
2a1ad637
FT
222static void
223dsp_cdevinfo_alloc(struct cdev *dev,
224 struct pcm_channel *rdch, struct pcm_channel *wrch,
225 struct pcm_channel *volch)
984263bc 226{
2a1ad637
FT
227 struct snddev_info *d;
228 struct dsp_cdevinfo *cdi;
229 int simplex;
558a398b 230
2a1ad637
FT
231 d = dsp_get_info(dev);
232
233 KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL &&
234 ((rdch == NULL && wrch == NULL) || rdch != wrch),
235 ("bogus %s(), what are you trying to accomplish here?", __func__));
236 PCM_BUSYASSERT(d);
237 PCM_LOCKASSERT(d);
238
239 simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0;
240
241 /*
242 * Scan for free instance entry and put it into the end of list.
243 * Create new one if necessary.
244 */
245 TAILQ_FOREACH(cdi, &d->dsp_cdevinfo_pool, link) {
246 if (cdi->busy != 0)
af8f7a68 247 break;
2a1ad637
FT
248 cdi->rdch = rdch;
249 cdi->wrch = wrch;
250 cdi->volch = volch;
251 cdi->simplex = simplex;
252 cdi->busy = 1;
253 TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
254 TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
255 dev->si_drv1 = cdi;
256 return;
af8f7a68 257 }
2a1ad637 258 PCM_UNLOCK(d);
67931cc4 259 cdi = kmalloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
2a1ad637
FT
260 PCM_LOCK(d);
261 cdi->rdch = rdch;
262 cdi->wrch = wrch;
263 cdi->volch = volch;
264 cdi->simplex = simplex;
265 cdi->busy = 1;
266 TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
267 dev->si_drv1 = cdi;
268}
984263bc 269
2a1ad637
FT
270static void
271dsp_cdevinfo_free(struct cdev *dev)
272{
273 struct snddev_info *d;
274 struct dsp_cdevinfo *cdi, *tmp;
275 uint32_t flags;
276 int i;
277
278 d = dsp_get_info(dev);
279
280 KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL &&
281 PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL &&
282 PCM_VOLCH(dev) == NULL,
283 ("bogus %s(), what are you trying to accomplish here?", __func__));
284 PCM_BUSYASSERT(d);
285 PCM_LOCKASSERT(d);
286
287 cdi = dev->si_drv1;
288 dev->si_drv1 = NULL;
289 cdi->rdch = NULL;
290 cdi->wrch = NULL;
291 cdi->volch = NULL;
292 cdi->simplex = 0;
293 cdi->busy = 0;
294
295 /*
296 * Once it is free, move it back to the beginning of list for
297 * faster new entry allocation.
298 */
299 TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
300 TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
301
302 /*
303 * Scan the list, cache free entries up to DSP_CDEVINFO_CACHESIZE.
304 * Reset simplex flags.
305 */
306 flags = dsp_get_flags(dev) & ~SD_F_PRIO_SET;
307 i = DSP_CDEVINFO_CACHESIZE;
67931cc4 308 TAILQ_FOREACH_MUTABLE(cdi, &d->dsp_cdevinfo_pool, link, tmp) {
2a1ad637
FT
309 if (cdi->busy != 0) {
310 if (cdi->simplex == 0) {
311 if (cdi->rdch != NULL)
312 flags |= SD_F_PRIO_RD;
313 if (cdi->wrch != NULL)
314 flags |= SD_F_PRIO_WR;
315 }
316 } else {
317 if (i == 0) {
318 TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
67931cc4 319 kfree(cdi, M_DEVBUF);
2a1ad637
FT
320 } else
321 i--;
322 }
af8f7a68 323 }
2a1ad637
FT
324 dsp_set_flags(dev, flags);
325}
984263bc 326
2a1ad637
FT
327void
328dsp_cdevinfo_init(struct snddev_info *d)
329{
330 struct dsp_cdevinfo *cdi;
331 int i;
332
333 KASSERT(d != NULL, ("NULL snddev_info"));
334 PCM_BUSYASSERT(d);
335 PCM_UNLOCKASSERT(d);
336
337 TAILQ_INIT(&d->dsp_cdevinfo_pool);
338 for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) {
67931cc4 339 cdi = kmalloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
2a1ad637 340 TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
af8f7a68 341 }
2a1ad637 342}
984263bc 343
2a1ad637
FT
344void
345dsp_cdevinfo_flush(struct snddev_info *d)
346{
347 struct dsp_cdevinfo *cdi, *tmp;
348
349 KASSERT(d != NULL, ("NULL snddev_info"));
350 PCM_BUSYASSERT(d);
351 PCM_UNLOCKASSERT(d);
352
353 cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool);
354 while (cdi != NULL) {
355 tmp = TAILQ_NEXT(cdi, link);
67931cc4 356 kfree(cdi, M_DEVBUF);
2a1ad637 357 cdi = tmp;
984263bc 358 }
2a1ad637
FT
359 TAILQ_INIT(&d->dsp_cdevinfo_pool);
360}
361
362/* duplex / simplex cdev type */
363enum {
364 DSP_CDEV_TYPE_RDONLY, /* simplex read-only (record) */
365 DSP_CDEV_TYPE_WRONLY, /* simplex write-only (play) */
366 DSP_CDEV_TYPE_RDWR /* duplex read, write, or both */
367};
984263bc 368
2a1ad637
FT
369enum {
370 DSP_CDEV_VOLCTL_NONE,
371 DSP_CDEV_VOLCTL_READ,
372 DSP_CDEV_VOLCTL_WRITE
373};
af8f7a68 374
2a1ad637
FT
375#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE))
376#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
377#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
378#define DSP_F_READ(x) ((x) & FREAD)
379#define DSP_F_WRITE(x) ((x) & FWRITE)
380
381static const struct {
382 int type;
383 char *name;
384 char *sep;
385 char *alias;
386 int use_sep;
387 int hw;
388 int max;
389 int volctl;
390 uint32_t fmt, spd;
391 int query;
392} dsp_cdevs[] = {
393 { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0,
394 SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
395 DSP_CDEV_TYPE_RDWR },
396 { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0,
397 SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED,
398 DSP_CDEV_TYPE_RDWR },
399 { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0,
400 SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED,
401 DSP_CDEV_TYPE_RDWR },
402 { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1,
403 SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY },
404 { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1,
405 SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY },
406 { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1,
407 SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY },
408 { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1,
409 SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY },
410 { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0,
411 SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR },
412 /* Low priority, OSSv4 aliases. */
413 { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0,
414 SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
415 DSP_CDEV_TYPE_RDWR },
416 { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0,
417 SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
418 DSP_CDEV_TYPE_RDWR },
419 { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0,
420 SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
421 DSP_CDEV_TYPE_RDWR },
422 { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0,
423 SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
424 DSP_CDEV_TYPE_RDWR },
425 { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0,
426 SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED,
427 DSP_CDEV_TYPE_RDWR },
428};
984263bc 429
2a1ad637
FT
430#define DSP_FIXUP_ERROR() do { \
431 prio = dsp_get_flags(i_dev); \
432 if (!DSP_F_VALID(flags)) \
433 error = EINVAL; \
434 if (!DSP_F_DUPLEX(flags) && \
435 ((DSP_F_READ(flags) && d->reccount == 0) || \
436 (DSP_F_WRITE(flags) && d->playcount == 0))) \
437 error = ENOTSUP; \
438 else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) && \
439 ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) || \
440 (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD)))) \
441 error = EBUSY; \
442 else if (DSP_REGISTERED(d, i_dev)) \
443 error = EBUSY; \
444} while (0)
984263bc 445
2a1ad637 446static int
88a31edf 447dsp_open(struct dev_open_args *ap)
2a1ad637 448{
88a31edf
FT
449 struct cdev *i_dev = ap->a_head.a_dev;
450 int flags = ap->a_oflags;
2a1ad637
FT
451 struct pcm_channel *rdch, *wrch;
452 struct snddev_info *d;
453 uint32_t fmt, spd, prio, volctl;
454 int i, error, rderror, wrerror, devtype, wdevunit, rdevunit;
455
456 /* Kind of impossible.. */
a9dbef8b 457 if (i_dev == NULL)
2a1ad637
FT
458 return (ENODEV);
459
460 d = dsp_get_info(i_dev);
461 if (!PCM_REGISTERED(d))
462 return (EBADF);
463
464 PCM_GIANT_ENTER(d);
465
466 /* Lock snddev so nobody else can monkey with it. */
467 PCM_LOCK(d);
468 PCM_WAIT(d);
469
470 /*
471 * Try to acquire cloned device before someone else pick it.
472 * ENODEV means this is not a cloned droids.
473 */
474 error = snd_clone_acquire(i_dev);
475 if (!(error == 0 || error == ENODEV)) {
476 DSP_FIXUP_ERROR();
477 PCM_UNLOCK(d);
478 PCM_GIANT_EXIT(d);
479 return (error);
984263bc
MD
480 }
481
2a1ad637
FT
482 error = 0;
483 DSP_FIXUP_ERROR();
484
485 if (error != 0) {
486 (void)snd_clone_release(i_dev);
487 PCM_UNLOCK(d);
488 PCM_GIANT_EXIT(d);
489 return (error);
490 }
491
492 /*
493 * That is just enough. Acquire and unlock pcm lock so
494 * the other will just have to wait until we finish doing
495 * everything.
496 */
497 PCM_ACQUIRE(d);
498 PCM_UNLOCK(d);
499
500 devtype = PCMDEV(i_dev);
501 wdevunit = -1;
502 rdevunit = -1;
503 fmt = 0;
504 spd = 0;
505 volctl = DSP_CDEV_VOLCTL_NONE;
506
507 for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
508 if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL)
509 continue;
510 /*
511 * Volume control only valid for DSPHW devices,
512 * and it must be opened in opposite direction be it
513 * simplex or duplex. Anything else will be handled
514 * as usual.
515 */
516 if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) {
517 if (dsp_cdevs[i].volctl != 0 &&
518 DSP_F_READ(flags)) {
519 volctl = DSP_CDEV_VOLCTL_WRITE;
520 flags &= ~FREAD;
521 flags |= FWRITE;
522 }
523 if (DSP_F_READ(flags)) {
524 (void)snd_clone_release(i_dev);
525 PCM_RELEASE_QUICK(d);
526 PCM_GIANT_EXIT(d);
527 return (ENOTSUP);
528 }
529 wdevunit = dev2unit(i_dev);
530 } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) {
531 if (dsp_cdevs[i].volctl != 0 &&
532 DSP_F_WRITE(flags)) {
533 volctl = DSP_CDEV_VOLCTL_READ;
534 flags &= ~FWRITE;
535 flags |= FREAD;
536 }
537 if (DSP_F_WRITE(flags)) {
538 (void)snd_clone_release(i_dev);
539 PCM_RELEASE_QUICK(d);
540 PCM_GIANT_EXIT(d);
541 return (ENOTSUP);
542 }
543 rdevunit = dev2unit(i_dev);
544 }
545 fmt = dsp_cdevs[i].fmt;
546 spd = dsp_cdevs[i].spd;
547 break;
548 }
549
2a1ad637
FT
550 rdch = NULL;
551 wrch = NULL;
552 rderror = 0;
553 wrerror = 0;
554
558a398b
SS
555 /*
556 * if we get here, the open request is valid- either:
557 * * we were previously not open
558 * * we were open for play xor record and the opener wants
559 * the non-open direction
560 */
2a1ad637 561 if (DSP_F_READ(flags)) {
558a398b 562 /* open for read */
2a1ad637 563 rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
a9dbef8b 564 curproc->p_pid, curproc->p_comm, rdevunit);
984263bc 565
2a1ad637
FT
566 if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0)
567 rderror = ENXIO;
984263bc 568
2a1ad637
FT
569 if (volctl == DSP_CDEV_VOLCTL_READ)
570 rderror = 0;
571
572 if (rderror != 0) {
573 if (rdch != NULL)
984263bc 574 pcm_chnrelease(rdch);
2a1ad637
FT
575 if (!DSP_F_DUPLEX(flags)) {
576 (void)snd_clone_release(i_dev);
577 PCM_RELEASE_QUICK(d);
578 PCM_GIANT_EXIT(d);
579 return (rderror);
580 }
581 rdch = NULL;
582 } else if (volctl == DSP_CDEV_VOLCTL_READ) {
583 if (rdch != NULL) {
584 pcm_chnref(rdch, 1);
585 pcm_chnrelease(rdch);
586 }
587 } else {
588 if (flags & O_NONBLOCK)
589 rdch->flags |= CHN_F_NBIO;
590 if (flags & O_EXCL)
591 rdch->flags |= CHN_F_EXCLUSIVE;
592 pcm_chnref(rdch, 1);
593 if (volctl == DSP_CDEV_VOLCTL_NONE)
594 chn_vpc_reset(rdch, SND_VOL_C_PCM, 0);
595 CHN_UNLOCK(rdch);
984263bc 596 }
2a1ad637 597 }
984263bc 598
2a1ad637
FT
599 if (DSP_F_WRITE(flags)) {
600 /* open for write */
601 wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
a9dbef8b 602 curproc->p_pid, curproc->p_comm, wdevunit);
2a1ad637
FT
603
604 if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0)
605 wrerror = ENXIO;
606
607 if (volctl == DSP_CDEV_VOLCTL_WRITE)
608 wrerror = 0;
609
610 if (wrerror != 0) {
611 if (wrch != NULL)
612 pcm_chnrelease(wrch);
613 if (!DSP_F_DUPLEX(flags)) {
614 if (rdch != NULL) {
615 /*
616 * Lock, deref and release previously
617 * created record channel
618 */
619 CHN_LOCK(rdch);
620 pcm_chnref(rdch, -1);
621 pcm_chnrelease(rdch);
622 }
623 (void)snd_clone_release(i_dev);
624 PCM_RELEASE_QUICK(d);
625 PCM_GIANT_EXIT(d);
626 return (wrerror);
627 }
628 wrch = NULL;
629 } else if (volctl == DSP_CDEV_VOLCTL_WRITE) {
630 if (wrch != NULL) {
631 pcm_chnref(wrch, 1);
632 pcm_chnrelease(wrch);
633 }
634 } else {
635 if (flags & O_NONBLOCK)
636 wrch->flags |= CHN_F_NBIO;
637 if (flags & O_EXCL)
638 wrch->flags |= CHN_F_EXCLUSIVE;
639 pcm_chnref(wrch, 1);
640 if (volctl == DSP_CDEV_VOLCTL_NONE)
641 chn_vpc_reset(wrch, SND_VOL_C_PCM, 0);
642 CHN_UNLOCK(wrch);
643 }
984263bc 644 }
558a398b 645
558a398b 646
2a1ad637 647 PCM_LOCK(d);
558a398b 648
2a1ad637
FT
649 /*
650 * We're done. Allocate channels information for this cdev.
651 */
652 switch (volctl) {
653 case DSP_CDEV_VOLCTL_READ:
654 KASSERT(wrch == NULL, ("wrch=%p not null!", wrch));
655 dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch);
656 break;
657 case DSP_CDEV_VOLCTL_WRITE:
658 KASSERT(rdch == NULL, ("rdch=%p not null!", rdch));
659 dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch);
660 break;
661 case DSP_CDEV_VOLCTL_NONE:
662 default:
663 if (wrch == NULL && rdch == NULL) {
664 (void)snd_clone_release(i_dev);
665 PCM_RELEASE(d);
666 PCM_UNLOCK(d);
667 PCM_GIANT_EXIT(d);
668 if (wrerror != 0)
669 return (wrerror);
670 if (rderror != 0)
671 return (rderror);
672 return (EINVAL);
984263bc 673 }
2a1ad637
FT
674 dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL);
675 if (rdch != NULL)
676 CHN_INSERT_HEAD(d, rdch, channels.pcm.opened);
677 if (wrch != NULL)
678 CHN_INSERT_HEAD(d, wrch, channels.pcm.opened);
679 break;
984263bc 680 }
558a398b 681
2a1ad637
FT
682 /*
683 * Increase clone refcount for its automatic garbage collector.
684 */
685 (void)snd_clone_ref(i_dev);
558a398b 686
2a1ad637
FT
687 PCM_RELEASE(d);
688 PCM_UNLOCK(d);
af8f7a68 689
2a1ad637 690 PCM_GIANT_LEAVE(d);
af8f7a68 691
2a1ad637 692 return (0);
984263bc
MD
693}
694
695static int
88a31edf 696dsp_close(struct dev_close_args *ap)
984263bc 697{
88a31edf 698 struct cdev *i_dev = ap->a_head.a_dev;
2a1ad637 699 struct pcm_channel *rdch, *wrch, *volch;
984263bc 700 struct snddev_info *d;
2a1ad637 701 int sg_ids, rdref, wdref;
984263bc 702
558a398b 703 d = dsp_get_info(i_dev);
2a1ad637
FT
704 if (!DSP_REGISTERED(d, i_dev))
705 return (EBADF);
706
707 PCM_GIANT_ENTER(d);
708
709 PCM_LOCK(d);
710 PCM_WAIT(d);
711 PCM_ACQUIRE(d);
712
713 rdch = PCM_RDCH(i_dev);
714 wrch = PCM_WRCH(i_dev);
715 volch = PCM_VOLCH(i_dev);
716
717 PCM_RDCH(i_dev) = NULL;
718 PCM_WRCH(i_dev) = NULL;
719 PCM_VOLCH(i_dev) = NULL;
720
721 rdref = -1;
722 wdref = -1;
723
724 if (volch != NULL) {
725 if (volch == rdch)
726 rdref--;
727 else if (volch == wrch)
728 wdref--;
729 else {
730 CHN_LOCK(volch);
731 pcm_chnref(volch, -1);
732 CHN_UNLOCK(volch);
733 }
af8f7a68 734 }
af8f7a68 735
2a1ad637
FT
736 if (rdch != NULL)
737 CHN_REMOVE(d, rdch, channels.pcm.opened);
738 if (wrch != NULL)
739 CHN_REMOVE(d, wrch, channels.pcm.opened);
740
741 if (rdch != NULL || wrch != NULL) {
742 PCM_UNLOCK(d);
743 if (rdch != NULL) {
744 /*
745 * The channel itself need not be locked because:
746 * a) Adding a channel to a syncgroup happens only
747 * in dsp_ioctl(), which cannot run concurrently
748 * to dsp_close().
749 * b) The syncmember pointer (sm) is protected by
750 * the global syncgroup list lock.
751 * c) A channel can't just disappear, invalidating
752 * pointers, unless it's closed/dereferenced
753 * first.
754 */
755 PCM_SG_LOCK();
756 sg_ids = chn_syncdestroy(rdch);
757 PCM_SG_UNLOCK();
758 if (sg_ids != 0)
759 free_unr(pcmsg_unrhdr, sg_ids);
984263bc 760
558a398b 761 CHN_LOCK(rdch);
2a1ad637 762 pcm_chnref(rdch, rdref);
558a398b 763 chn_abort(rdch); /* won't sleep */
2a1ad637
FT
764 rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
765 CHN_F_DEAD | CHN_F_EXCLUSIVE);
766 chn_reset(rdch, 0, 0);
558a398b 767 pcm_chnrelease(rdch);
984263bc 768 }
2a1ad637 769 if (wrch != NULL) {
558a398b 770 /*
2a1ad637 771 * Please see block above.
558a398b 772 */
2a1ad637
FT
773 PCM_SG_LOCK();
774 sg_ids = chn_syncdestroy(wrch);
775 PCM_SG_UNLOCK();
776 if (sg_ids != 0)
777 free_unr(pcmsg_unrhdr, sg_ids);
778
779 CHN_LOCK(wrch);
780 pcm_chnref(wrch, wdref);
558a398b 781 chn_flush(wrch); /* may sleep */
2a1ad637
FT
782 wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
783 CHN_F_DEAD | CHN_F_EXCLUSIVE);
784 chn_reset(wrch, 0, 0);
558a398b 785 pcm_chnrelease(wrch);
984263bc 786 }
2a1ad637
FT
787 PCM_LOCK(d);
788 }
984263bc 789
2a1ad637
FT
790 dsp_cdevinfo_free(i_dev);
791 /*
792 * Release clone busy state and unref it so the automatic
793 * garbage collector will get the hint and do the remaining
794 * cleanup process.
795 */
796 (void)snd_clone_release(i_dev);
797
798 /*
799 * destroy_dev() might sleep, so release pcm lock
800 * here and rely on pcm cv serialization.
801 */
802 PCM_UNLOCK(d);
803 (void)snd_clone_unref(i_dev);
804 PCM_LOCK(d);
805
806 PCM_RELEASE(d);
807 PCM_UNLOCK(d);
808
809 PCM_GIANT_LEAVE(d);
810
811 return (0);
812}
813
814static __inline int
815dsp_io_ops(struct cdev *i_dev, struct uio *buf)
816{
817 struct snddev_info *d;
818 struct pcm_channel **ch, *rdch, *wrch;
819 int (*chn_io)(struct pcm_channel *, struct uio *);
820 int prio, ret;
821 pid_t runpid;
822
823 KASSERT(i_dev != NULL && buf != NULL &&
824 (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE),
825 ("%s(): io train wreck!", __func__));
826
827 d = dsp_get_info(i_dev);
828 if (!DSP_REGISTERED(d, i_dev))
829 return (EBADF);
830
831 PCM_GIANT_ENTER(d);
832
833 switch (buf->uio_rw) {
834 case UIO_READ:
835 prio = SD_F_PRIO_RD;
836 ch = &rdch;
837 chn_io = chn_read;
838 break;
839 case UIO_WRITE:
840 prio = SD_F_PRIO_WR;
841 ch = &wrch;
842 chn_io = chn_write;
843 break;
844 default:
845 panic("invalid/corrupted uio direction: %d", buf->uio_rw);
846 break;
847 }
848
849 rdch = NULL;
850 wrch = NULL;
851 runpid = buf->uio_td->td_proc->p_pid;
852
853 getchns(i_dev, &rdch, &wrch, prio);
854
855 if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) {
856 PCM_GIANT_EXIT(d);
857 return (EBADF);
858 }
859
860 if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) ||
861 (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) {
862 relchns(i_dev, rdch, wrch, prio);
863 PCM_GIANT_EXIT(d);
864 return (EINVAL);
865 } else if (!((*ch)->flags & CHN_F_RUNNING)) {
866 (*ch)->flags |= CHN_F_RUNNING;
867 (*ch)->pid = runpid;
984263bc 868 }
2a1ad637
FT
869
870 /*
871 * chn_read/write must give up channel lock in order to copy bytes
872 * from/to userland, so up the "in progress" counter to make sure
873 * someone else doesn't come along and muss up the buffer.
874 */
875 ++(*ch)->inprog;
876 ret = chn_io(*ch, buf);
877 --(*ch)->inprog;
878
879 CHN_BROADCAST(&(*ch)->cv);
880
881 relchns(i_dev, rdch, wrch, prio);
882
883 PCM_GIANT_LEAVE(d);
884
885 return (ret);
984263bc
MD
886}
887
888static int
88a31edf 889dsp_read(struct dev_read_args *ap)
984263bc 890{
88a31edf
FT
891 struct cdev *i_dev = ap->a_head.a_dev;
892 struct uio *buf = ap->a_uio;
893
2a1ad637
FT
894 return (dsp_io_ops(i_dev, buf));
895}
896
897static int
88a31edf 898dsp_write(struct dev_write_args *ap)
2a1ad637 899{
88a31edf
FT
900 struct cdev *i_dev = ap->a_head.a_dev;
901 struct uio *buf = ap->a_uio;
902
2a1ad637
FT
903 return (dsp_io_ops(i_dev, buf));
904}
905
906static int
907dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch)
908{
909 struct snddev_info *d;
910 struct pcm_channel *c;
911 int unit;
984263bc 912
2a1ad637
FT
913 KASSERT(dev != NULL && volch != NULL,
914 ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch));
984263bc 915
2a1ad637
FT
916 d = dsp_get_info(dev);
917 if (!PCM_REGISTERED(d)) {
918 *volch = NULL;
919 return (EINVAL);
920 }
921
922 PCM_UNLOCKASSERT(d);
923
924 *volch = NULL;
925
926 c = PCM_VOLCH(dev);
927 if (c != NULL) {
928 if (!(c->feederflags & (1 << FEEDER_VOLUME)))
929 return (-1);
930 *volch = c;
931 return (0);
932 }
933
934 PCM_LOCK(d);
935 PCM_WAIT(d);
936 PCM_ACQUIRE(d);
937
938 unit = dev2unit(dev);
984263bc 939
2a1ad637
FT
940 CHN_FOREACH(c, d, channels.pcm) {
941 CHN_LOCK(c);
942 if (c->unit != unit) {
943 CHN_UNLOCK(c);
944 continue;
945 }
946 *volch = c;
947 pcm_chnref(c, 1);
948 PCM_VOLCH(dev) = c;
949 CHN_UNLOCK(c);
950 PCM_RELEASE(d);
951 PCM_UNLOCK(d);
952 return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1);
984263bc 953 }
984263bc 954
2a1ad637
FT
955 PCM_RELEASE(d);
956 PCM_UNLOCK(d);
957
958 return (EINVAL);
984263bc
MD
959}
960
961static int
2a1ad637
FT
962dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd,
963 caddr_t arg)
984263bc 964{
2a1ad637 965 struct snddev_info *d;
984263bc 966 struct pcm_channel *rdch, *wrch;
2a1ad637
FT
967 int j, devtype, ret;
968
969 d = dsp_get_info(dev);
970 if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC))
971 return (-1);
972
973 PCM_UNLOCKASSERT(d);
974
975 j = cmd & 0xff;
976
977 rdch = PCM_RDCH(dev);
978 wrch = PCM_WRCH(dev);
984263bc 979
2a1ad637
FT
980 /* No specific channel, look into cache */
981 if (volch == NULL)
982 volch = PCM_VOLCH(dev);
983
984 /* Look harder */
985 if (volch == NULL) {
986 if (j == SOUND_MIXER_RECLEV && rdch != NULL)
987 volch = rdch;
988 else if (j == SOUND_MIXER_PCM && wrch != NULL)
989 volch = wrch;
990 }
991
992 devtype = PCMDEV(dev);
993
994 /* Look super harder */
995 if (volch == NULL &&
996 (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY ||
997 devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) {
998 ret = dsp_get_volume_channel(dev, &volch);
999 if (ret != 0)
1000 return (ret);
1001 if (volch == NULL)
1002 return (EINVAL);
1003 }
984263bc 1004
2a1ad637
FT
1005 /* Final validation */
1006 if (volch != NULL) {
1007 CHN_LOCK(volch);
1008 if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
1009 CHN_UNLOCK(volch);
1010 return (-1);
1011 }
1012 if (volch->direction == PCMDIR_PLAY)
1013 wrch = volch;
1014 else
1015 rdch = volch;
1016 }
984263bc 1017
2a1ad637
FT
1018 ret = EINVAL;
1019
1020 if (volch != NULL &&
1021 ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) ||
1022 (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) {
1023 if ((cmd & ~0xff) == MIXER_WRITE(0)) {
1024 int left, right, center;
1025
1026 left = *(int *)arg & 0x7f;
1027 right = ((*(int *)arg) >> 8) & 0x7f;
1028 center = (left + right) >> 1;
1029 chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right,
1030 center);
1031 } else if ((cmd & ~0xff) == MIXER_READ(0)) {
1032 *(int *)arg = CHN_GETVOLUME(volch,
1033 SND_VOL_C_PCM, SND_CHN_T_FL);
1034 *(int *)arg |= CHN_GETVOLUME(volch,
1035 SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
1036 }
1037 ret = 0;
1038 } else if (rdch != NULL || wrch != NULL) {
1039 switch (j) {
1040 case SOUND_MIXER_DEVMASK:
1041 case SOUND_MIXER_CAPS:
1042 case SOUND_MIXER_STEREODEVS:
1043 if ((cmd & ~0xff) == MIXER_READ(0)) {
1044 *(int *)arg = 0;
1045 if (rdch != NULL)
1046 *(int *)arg |= SOUND_MASK_RECLEV;
1047 if (wrch != NULL)
1048 *(int *)arg |= SOUND_MASK_PCM;
1049 }
1050 ret = 0;
1051 break;
1052 case SOUND_MIXER_RECMASK:
1053 case SOUND_MIXER_RECSRC:
1054 if ((cmd & ~0xff) == MIXER_READ(0))
1055 *(int *)arg = 0;
1056 ret = 0;
1057 break;
1058 default:
1059 break;
1060 }
984263bc 1061 }
984263bc 1062
2a1ad637
FT
1063 if (volch != NULL)
1064 CHN_UNLOCK(volch);
1065
1066 return (ret);
984263bc
MD
1067}
1068
1069static int
88a31edf 1070dsp_ioctl(struct dev_ioctl_args *ap)
984263bc 1071{
88a31edf
FT
1072 struct cdev *i_dev = ap->a_head.a_dev;
1073 u_long cmd = ap->a_cmd;
1074 caddr_t arg = ap->a_data;
558a398b 1075 struct pcm_channel *chn, *rdch, *wrch;
984263bc 1076 struct snddev_info *d;
2a1ad637
FT
1077 u_long xcmd;
1078 int *arg_i, ret, tmp;
984263bc 1079
558a398b 1080 d = dsp_get_info(i_dev);
2a1ad637
FT
1081 if (!DSP_REGISTERED(d, i_dev))
1082 return (EBADF);
984263bc 1083
2a1ad637
FT
1084 PCM_GIANT_ENTER(d);
1085
1086 arg_i = (int *)arg;
1087 ret = 0;
1088 xcmd = 0;
1089 chn = NULL;
34e3e97f 1090
34e3e97f 1091 if (IOCGROUP(cmd) == 'M') {
2a1ad637
FT
1092 if (cmd == OSS_GETVERSION) {
1093 *arg_i = SOUND_VERSION;
1094 PCM_GIANT_EXIT(d);
1095 return (0);
1096 }
1097 ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg);
1098 if (ret != -1) {
1099 PCM_GIANT_EXIT(d);
1100 return (ret);
34e3e97f
SS
1101 }
1102
2a1ad637
FT
1103 if (d->mixer_dev != NULL) {
1104 PCM_ACQUIRE_QUICK(d);
a9dbef8b 1105 ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1,
2a1ad637
FT
1106 MIXER_CMD_DIRECT);
1107 PCM_RELEASE_QUICK(d);
1108 } else
1109 ret = EBADF;
1110
1111 PCM_GIANT_EXIT(d);
1112
1113 return (ret);
1114 }
1115
1116 /*
1117 * Certain ioctls may be made on any type of device (audio, mixer,
1118 * and MIDI). Handle those special cases here.
1119 */
1120 if (IOCGROUP(cmd) == 'X') {
1121 PCM_ACQUIRE_QUICK(d);
1122 switch(cmd) {
1123 case SNDCTL_SYSINFO:
1124 sound_oss_sysinfo((oss_sysinfo *)arg);
1125 break;
1126 case SNDCTL_CARDINFO:
1127 ret = sound_oss_card_info((oss_card_info *)arg);
1128 break;
1129 case SNDCTL_AUDIOINFO:
1130 case SNDCTL_AUDIOINFO_EX:
1131 case SNDCTL_ENGINEINFO:
1132 ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
1133 break;
1134 case SNDCTL_MIXERINFO:
1135 ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
1136 break;
1137 default:
1138 ret = EINVAL;
1139 }
1140 PCM_RELEASE_QUICK(d);
1141 PCM_GIANT_EXIT(d);
1142 return (ret);
1143 }
1144
1145 getchns(i_dev, &rdch, &wrch, 0);
1146
1147 if (wrch != NULL && (wrch->flags & CHN_F_DEAD))
1148 wrch = NULL;
1149 if (rdch != NULL && (rdch->flags & CHN_F_DEAD))
1150 rdch = NULL;
1151
1152 if (wrch == NULL && rdch == NULL) {
1153 PCM_GIANT_EXIT(d);
1154 return (EINVAL);
1155 }
1156
984263bc
MD
1157 switch(cmd) {
1158#ifdef OLDPCM_IOCTL
1159 /*
1160 * we start with the new ioctl interface.
1161 */
1162 case AIONWRITE: /* how many bytes can write ? */
558a398b
SS
1163 if (wrch) {
1164 CHN_LOCK(wrch);
984263bc
MD
1165/*
1166 if (wrch && wrch->bufhard.dl)
1167 while (chn_wrfeed(wrch) == 0);
1168*/
558a398b
SS
1169 *arg_i = sndbuf_getfree(wrch->bufsoft);
1170 CHN_UNLOCK(wrch);
1171 } else {
1172 *arg_i = 0;
1173 ret = EINVAL;
1174 }
984263bc
MD
1175 break;
1176
1177 case AIOSSIZE: /* set the current blocksize */
1178 {
1179 struct snd_size *p = (struct snd_size *)arg;
1180
1181 p->play_size = 0;
1182 p->rec_size = 0;
2a1ad637 1183 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1184 if (wrch) {
1185 CHN_LOCK(wrch);
1186 chn_setblocksize(wrch, 2, p->play_size);
1187 p->play_size = sndbuf_getblksz(wrch->bufsoft);
1188 CHN_UNLOCK(wrch);
1189 }
1190 if (rdch) {
1191 CHN_LOCK(rdch);
1192 chn_setblocksize(rdch, 2, p->rec_size);
1193 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
1194 CHN_UNLOCK(rdch);
1195 }
2a1ad637 1196 PCM_RELEASE_QUICK(d);
984263bc
MD
1197 }
1198 break;
1199 case AIOGSIZE: /* get the current blocksize */
1200 {
1201 struct snd_size *p = (struct snd_size *)arg;
1202
558a398b
SS
1203 if (wrch) {
1204 CHN_LOCK(wrch);
984263bc 1205 p->play_size = sndbuf_getblksz(wrch->bufsoft);
558a398b
SS
1206 CHN_UNLOCK(wrch);
1207 }
1208 if (rdch) {
1209 CHN_LOCK(rdch);
984263bc 1210 p->rec_size = sndbuf_getblksz(rdch->bufsoft);
558a398b
SS
1211 CHN_UNLOCK(rdch);
1212 }
984263bc
MD
1213 }
1214 break;
1215
1216 case AIOSFMT:
558a398b 1217 case AIOGFMT:
984263bc
MD
1218 {
1219 snd_chan_param *p = (snd_chan_param *)arg;
1220
558a398b
SS
1221 if (cmd == AIOSFMT &&
1222 ((p->play_format != 0 && p->play_rate == 0) ||
1223 (p->rec_format != 0 && p->rec_rate == 0))) {
1224 ret = EINVAL;
1225 break;
1226 }
2a1ad637 1227 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1228 if (wrch) {
1229 CHN_LOCK(wrch);
558a398b 1230 if (cmd == AIOSFMT && p->play_format != 0) {
2a1ad637
FT
1231 chn_setformat(wrch,
1232 SND_FORMAT(p->play_format,
1233 AFMT_CHANNEL(wrch->format),
1234 AFMT_EXTCHANNEL(wrch->format)));
558a398b
SS
1235 chn_setspeed(wrch, p->play_rate);
1236 }
1237 p->play_rate = wrch->speed;
2a1ad637 1238 p->play_format = AFMT_ENCODING(wrch->format);
984263bc 1239 CHN_UNLOCK(wrch);
558a398b
SS
1240 } else {
1241 p->play_rate = 0;
1242 p->play_format = 0;
984263bc
MD
1243 }
1244 if (rdch) {
1245 CHN_LOCK(rdch);
558a398b 1246 if (cmd == AIOSFMT && p->rec_format != 0) {
2a1ad637
FT
1247 chn_setformat(rdch,
1248 SND_FORMAT(p->rec_format,
1249 AFMT_CHANNEL(rdch->format),
1250 AFMT_EXTCHANNEL(rdch->format)));
558a398b
SS
1251 chn_setspeed(rdch, p->rec_rate);
1252 }
1253 p->rec_rate = rdch->speed;
2a1ad637 1254 p->rec_format = AFMT_ENCODING(rdch->format);
984263bc 1255 CHN_UNLOCK(rdch);
558a398b
SS
1256 } else {
1257 p->rec_rate = 0;
1258 p->rec_format = 0;
984263bc 1259 }
2a1ad637 1260 PCM_RELEASE_QUICK(d);
984263bc 1261 }
984263bc
MD
1262 break;
1263
1264 case AIOGCAP: /* get capabilities */
1265 {
1266 snd_capabilities *p = (snd_capabilities *)arg;
1267 struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
558a398b 1268 struct cdev *pdev;
984263bc 1269
2a1ad637 1270 PCM_LOCK(d);
984263bc
MD
1271 if (rdch) {
1272 CHN_LOCK(rdch);
1273 rcaps = chn_getcaps(rdch);
1274 }
1275 if (wrch) {
1276 CHN_LOCK(wrch);
1277 pcaps = chn_getcaps(wrch);
1278 }
1279 p->rate_min = max(rcaps? rcaps->minspeed : 0,
1280 pcaps? pcaps->minspeed : 0);
1281 p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
1282 pcaps? pcaps->maxspeed : 1000000);
1283 p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
1284 wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
1285 /* XXX bad on sb16 */
1286 p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
1287 (wrch? chn_getformats(wrch) : 0xffffffff);
1288 if (rdch && wrch)
558a398b
SS
1289 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
1290 pdev = d->mixer_dev;
984263bc
MD
1291 p->mixers = 1; /* default: one mixer */
1292 p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
1293 p->left = p->right = 100;
558a398b
SS
1294 if (wrch)
1295 CHN_UNLOCK(wrch);
2a1ad637
FT
1296 if (rdch)
1297 CHN_UNLOCK(rdch);
1298 PCM_UNLOCK(d);
984263bc
MD
1299 }
1300 break;
1301
1302 case AIOSTOP:
558a398b
SS
1303 if (*arg_i == AIOSYNC_PLAY && wrch) {
1304 CHN_LOCK(wrch);
984263bc 1305 *arg_i = chn_abort(wrch);
558a398b
SS
1306 CHN_UNLOCK(wrch);
1307 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
1308 CHN_LOCK(rdch);
984263bc 1309 *arg_i = chn_abort(rdch);
558a398b
SS
1310 CHN_UNLOCK(rdch);
1311 } else {
67931cc4 1312 kprintf("AIOSTOP: bad channel 0x%x\n", *arg_i);
984263bc
MD
1313 *arg_i = 0;
1314 }
1315 break;
1316
1317 case AIOSYNC:
67931cc4 1318 kprintf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
984263bc
MD
1319 ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
1320 break;
1321#endif
1322 /*
1323 * here follow the standard ioctls (filio.h etc.)
1324 */
1325 case FIONREAD: /* get # bytes to read */
558a398b
SS
1326 if (rdch) {
1327 CHN_LOCK(rdch);
1328/* if (rdch && rdch->bufhard.dl)
1329 while (chn_rdfeed(rdch) == 0);
1330*/
1331 *arg_i = sndbuf_getready(rdch->bufsoft);
1332 CHN_UNLOCK(rdch);
1333 } else {
1334 *arg_i = 0;
1335 ret = EINVAL;
1336 }
984263bc
MD
1337 break;
1338
1339 case FIOASYNC: /*set/clear async i/o */
67931cc4 1340 DEB( kprintf("FIOASYNC\n") ; )
984263bc
MD
1341 break;
1342
2a1ad637 1343 case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */
558a398b
SS
1344 case FIONBIO: /* set/clear non-blocking i/o */
1345 if (rdch) {
1346 CHN_LOCK(rdch);
2a1ad637 1347 if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
984263bc 1348 rdch->flags |= CHN_F_NBIO;
558a398b
SS
1349 else
1350 rdch->flags &= ~CHN_F_NBIO;
1351 CHN_UNLOCK(rdch);
1352 }
1353 if (wrch) {
1354 CHN_LOCK(wrch);
2a1ad637 1355 if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
984263bc 1356 wrch->flags |= CHN_F_NBIO;
558a398b
SS
1357 else
1358 wrch->flags &= ~CHN_F_NBIO;
1359 CHN_UNLOCK(wrch);
984263bc
MD
1360 }
1361 break;
1362
1363 /*
1364 * Finally, here is the linux-compatible ioctl interface
1365 */
1366#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
1367 case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
1368 case SNDCTL_DSP_GETBLKSIZE:
558a398b
SS
1369 chn = wrch ? wrch : rdch;
1370 if (chn) {
1371 CHN_LOCK(chn);
1372 *arg_i = sndbuf_getblksz(chn->bufsoft);
1373 CHN_UNLOCK(chn);
1374 } else {
984263bc 1375 *arg_i = 0;
558a398b
SS
1376 ret = EINVAL;
1377 }
2a1ad637 1378 break;
984263bc
MD
1379
1380 case SNDCTL_DSP_SETBLKSIZE:
1381 RANGE(*arg_i, 16, 65536);
2a1ad637 1382 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1383 if (wrch) {
1384 CHN_LOCK(wrch);
1385 chn_setblocksize(wrch, 2, *arg_i);
1386 CHN_UNLOCK(wrch);
1387 }
1388 if (rdch) {
1389 CHN_LOCK(rdch);
1390 chn_setblocksize(rdch, 2, *arg_i);
1391 CHN_UNLOCK(rdch);
1392 }
2a1ad637 1393 PCM_RELEASE_QUICK(d);
984263bc
MD
1394 break;
1395
1396 case SNDCTL_DSP_RESET:
67931cc4 1397 DEB(kprintf("dsp reset\n"));
558a398b
SS
1398 if (wrch) {
1399 CHN_LOCK(wrch);
984263bc 1400 chn_abort(wrch);
558a398b
SS
1401 chn_resetbuf(wrch);
1402 CHN_UNLOCK(wrch);
1403 }
1404 if (rdch) {
1405 CHN_LOCK(rdch);
984263bc 1406 chn_abort(rdch);
558a398b
SS
1407 chn_resetbuf(rdch);
1408 CHN_UNLOCK(rdch);
1409 }
984263bc
MD
1410 break;
1411
1412 case SNDCTL_DSP_SYNC:
67931cc4 1413 DEB(kprintf("dsp sync\n"));
984263bc
MD
1414 /* chn_sync may sleep */
1415 if (wrch) {
1416 CHN_LOCK(wrch);
2a1ad637 1417 chn_sync(wrch, 0);
984263bc
MD
1418 CHN_UNLOCK(wrch);
1419 }
1420 break;
1421
1422 case SNDCTL_DSP_SPEED:
1423 /* chn_setspeed may sleep */
1424 tmp = 0;
2a1ad637 1425 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1426 if (wrch) {
1427 CHN_LOCK(wrch);
1428 ret = chn_setspeed(wrch, *arg_i);
1429 tmp = wrch->speed;
1430 CHN_UNLOCK(wrch);
1431 }
1432 if (rdch && ret == 0) {
1433 CHN_LOCK(rdch);
1434 ret = chn_setspeed(rdch, *arg_i);
1435 if (tmp == 0)
1436 tmp = rdch->speed;
1437 CHN_UNLOCK(rdch);
1438 }
2a1ad637 1439 PCM_RELEASE_QUICK(d);
984263bc
MD
1440 *arg_i = tmp;
1441 break;
1442
1443 case SOUND_PCM_READ_RATE:
558a398b
SS
1444 chn = wrch ? wrch : rdch;
1445 if (chn) {
1446 CHN_LOCK(chn);
1447 *arg_i = chn->speed;
1448 CHN_UNLOCK(chn);
1449 } else {
1450 *arg_i = 0;
1451 ret = EINVAL;
1452 }
984263bc
MD
1453 break;
1454
1455 case SNDCTL_DSP_STEREO:
1456 tmp = -1;
2a1ad637
FT
1457 *arg_i = (*arg_i)? 2 : 1;
1458 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1459 if (wrch) {
1460 CHN_LOCK(wrch);
2a1ad637
FT
1461 ret = chn_setformat(wrch,
1462 SND_FORMAT(wrch->format, *arg_i, 0));
1463 tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0;
984263bc
MD
1464 CHN_UNLOCK(wrch);
1465 }
1466 if (rdch && ret == 0) {
1467 CHN_LOCK(rdch);
2a1ad637
FT
1468 ret = chn_setformat(rdch,
1469 SND_FORMAT(rdch->format, *arg_i, 0));
984263bc 1470 if (tmp == -1)
2a1ad637 1471 tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0;
984263bc
MD
1472 CHN_UNLOCK(rdch);
1473 }
2a1ad637 1474 PCM_RELEASE_QUICK(d);
984263bc
MD
1475 *arg_i = tmp;
1476 break;
1477
1478 case SOUND_PCM_WRITE_CHANNELS:
1479/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
2a1ad637
FT
1480 if (*arg_i < 0) {
1481 *arg_i = 0;
1482 ret = EINVAL;
1483 break;
1484 }
984263bc 1485 if (*arg_i != 0) {
2a1ad637
FT
1486 struct pcmchan_matrix *m;
1487 uint32_t ext;
1488
984263bc 1489 tmp = 0;
2a1ad637
FT
1490 if (*arg_i > SND_CHN_MAX)
1491 *arg_i = SND_CHN_MAX;
1492
1493 m = feeder_matrix_default_channel_map(*arg_i);
1494 if (m != NULL)
1495 ext = m->ext;
1496 else
1497 ext = 0;
1498
1499 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1500 if (wrch) {
1501 CHN_LOCK(wrch);
2a1ad637
FT
1502 ret = chn_setformat(wrch,
1503 SND_FORMAT(wrch->format, *arg_i, ext));
1504 tmp = AFMT_CHANNEL(wrch->format);
984263bc
MD
1505 CHN_UNLOCK(wrch);
1506 }
1507 if (rdch && ret == 0) {
1508 CHN_LOCK(rdch);
2a1ad637
FT
1509 ret = chn_setformat(rdch,
1510 SND_FORMAT(rdch->format, *arg_i, ext));
984263bc 1511 if (tmp == 0)
2a1ad637 1512 tmp = AFMT_CHANNEL(rdch->format);
984263bc
MD
1513 CHN_UNLOCK(rdch);
1514 }
2a1ad637 1515 PCM_RELEASE_QUICK(d);
984263bc
MD
1516 *arg_i = tmp;
1517 } else {
558a398b
SS
1518 chn = wrch ? wrch : rdch;
1519 CHN_LOCK(chn);
2a1ad637 1520 *arg_i = AFMT_CHANNEL(chn->format);
558a398b 1521 CHN_UNLOCK(chn);
984263bc
MD
1522 }
1523 break;
1524
1525 case SOUND_PCM_READ_CHANNELS:
558a398b
SS
1526 chn = wrch ? wrch : rdch;
1527 if (chn) {
1528 CHN_LOCK(chn);
2a1ad637 1529 *arg_i = AFMT_CHANNEL(chn->format);
558a398b
SS
1530 CHN_UNLOCK(chn);
1531 } else {
1532 *arg_i = 0;
1533 ret = EINVAL;
1534 }
984263bc
MD
1535 break;
1536
1537 case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
558a398b
SS
1538 chn = wrch ? wrch : rdch;
1539 if (chn) {
1540 CHN_LOCK(chn);
1541 *arg_i = chn_getformats(chn);
1542 CHN_UNLOCK(chn);
1543 } else {
1544 *arg_i = 0;
1545 ret = EINVAL;
1546 }
2a1ad637 1547 break;
984263bc
MD
1548
1549 case SNDCTL_DSP_SETFMT: /* sets _one_ format */
2a1ad637 1550 if (*arg_i != AFMT_QUERY) {
984263bc 1551 tmp = 0;
2a1ad637 1552 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1553 if (wrch) {
1554 CHN_LOCK(wrch);
2a1ad637
FT
1555 ret = chn_setformat(wrch, SND_FORMAT(*arg_i,
1556 AFMT_CHANNEL(wrch->format),
1557 AFMT_EXTCHANNEL(wrch->format)));
1558 tmp = wrch->format;
984263bc
MD
1559 CHN_UNLOCK(wrch);
1560 }
1561 if (rdch && ret == 0) {
1562 CHN_LOCK(rdch);
2a1ad637
FT
1563 ret = chn_setformat(rdch, SND_FORMAT(*arg_i,
1564 AFMT_CHANNEL(rdch->format),
1565 AFMT_EXTCHANNEL(rdch->format)));
984263bc 1566 if (tmp == 0)
2a1ad637 1567 tmp = rdch->format;
984263bc
MD
1568 CHN_UNLOCK(rdch);
1569 }
2a1ad637
FT
1570 PCM_RELEASE_QUICK(d);
1571 *arg_i = AFMT_ENCODING(tmp);
558a398b
SS
1572 } else {
1573 chn = wrch ? wrch : rdch;
1574 CHN_LOCK(chn);
2a1ad637 1575 *arg_i = AFMT_ENCODING(chn->format);
558a398b
SS
1576 CHN_UNLOCK(chn);
1577 }
984263bc
MD
1578 break;
1579
1580 case SNDCTL_DSP_SETFRAGMENT:
67931cc4 1581 DEB(kprintf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
984263bc 1582 {
2a1ad637
FT
1583 uint32_t fragln = (*arg_i) & 0x0000ffff;
1584 uint32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
1585 uint32_t fragsz;
1586 uint32_t r_maxfrags, r_fragsz;
984263bc
MD
1587
1588 RANGE(fragln, 4, 16);
1589 fragsz = 1 << fragln;
1590
1591 if (maxfrags == 0)
1592 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
558a398b
SS
1593 if (maxfrags < 2)
1594 maxfrags = 2;
984263bc
MD
1595 if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
1596 maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
1597
67931cc4 1598 DEB(kprintf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
2a1ad637 1599 PCM_ACQUIRE_QUICK(d);
984263bc
MD
1600 if (rdch) {
1601 CHN_LOCK(rdch);
1602 ret = chn_setblocksize(rdch, maxfrags, fragsz);
558a398b
SS
1603 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
1604 r_fragsz = sndbuf_getblksz(rdch->bufsoft);
984263bc 1605 CHN_UNLOCK(rdch);
558a398b
SS
1606 } else {
1607 r_maxfrags = maxfrags;
1608 r_fragsz = fragsz;
984263bc
MD
1609 }
1610 if (wrch && ret == 0) {
1611 CHN_LOCK(wrch);
1612 ret = chn_setblocksize(wrch, maxfrags, fragsz);
1613 maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
1614 fragsz = sndbuf_getblksz(wrch->bufsoft);
1615 CHN_UNLOCK(wrch);
558a398b
SS
1616 } else { /* use whatever came from the read channel */
1617 maxfrags = r_maxfrags;
1618 fragsz = r_fragsz;
984263bc 1619 }
2a1ad637 1620 PCM_RELEASE_QUICK(d);
984263bc
MD
1621
1622 fragln = 0;
1623 while (fragsz > 1) {
1624 fragln++;
1625 fragsz >>= 1;
1626 }
1627 *arg_i = (maxfrags << 16) | fragln;
1628 }
1629 break;
1630
1631 case SNDCTL_DSP_GETISPACE:
1632 /* return the size of data available in the input queue */
1633 {
1634 audio_buf_info *a = (audio_buf_info *)arg;
1635 if (rdch) {
1636 struct snd_dbuf *bs = rdch->bufsoft;
1637
1638 CHN_LOCK(rdch);
1639 a->bytes = sndbuf_getready(bs);
1640 a->fragments = a->bytes / sndbuf_getblksz(bs);
1641 a->fragstotal = sndbuf_getblkcnt(bs);
1642 a->fragsize = sndbuf_getblksz(bs);
1643 CHN_UNLOCK(rdch);
2a1ad637
FT
1644 } else
1645 ret = EINVAL;
984263bc
MD
1646 }
1647 break;
1648
1649 case SNDCTL_DSP_GETOSPACE:
1650 /* return space available in the output queue */
1651 {
1652 audio_buf_info *a = (audio_buf_info *)arg;
1653 if (wrch) {
1654 struct snd_dbuf *bs = wrch->bufsoft;
1655
1656 CHN_LOCK(wrch);
558a398b 1657 /* XXX abusive DMA update: chn_wrupdate(wrch); */
984263bc
MD
1658 a->bytes = sndbuf_getfree(bs);
1659 a->fragments = a->bytes / sndbuf_getblksz(bs);
1660 a->fragstotal = sndbuf_getblkcnt(bs);
1661 a->fragsize = sndbuf_getblksz(bs);
1662 CHN_UNLOCK(wrch);
2a1ad637
FT
1663 } else
1664 ret = EINVAL;
984263bc
MD
1665 }
1666 break;
1667
1668 case SNDCTL_DSP_GETIPTR:
1669 {
1670 count_info *a = (count_info *)arg;
1671 if (rdch) {
1672 struct snd_dbuf *bs = rdch->bufsoft;
1673
1674 CHN_LOCK(rdch);
558a398b 1675 /* XXX abusive DMA update: chn_rdupdate(rdch); */
984263bc
MD
1676 a->bytes = sndbuf_gettotal(bs);
1677 a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
2a1ad637 1678 a->ptr = sndbuf_getfreeptr(bs);
984263bc
MD
1679 rdch->blocks = sndbuf_getblocks(bs);
1680 CHN_UNLOCK(rdch);
1681 } else
1682 ret = EINVAL;
1683 }
1684 break;
1685
1686 case SNDCTL_DSP_GETOPTR:
1687 {
1688 count_info *a = (count_info *)arg;
1689 if (wrch) {
1690 struct snd_dbuf *bs = wrch->bufsoft;
1691
1692 CHN_LOCK(wrch);
558a398b 1693 /* XXX abusive DMA update: chn_wrupdate(wrch); */
984263bc
MD
1694 a->bytes = sndbuf_gettotal(bs);
1695 a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
1696 a->ptr = sndbuf_getreadyptr(bs);
1697 wrch->blocks = sndbuf_getblocks(bs);
1698 CHN_UNLOCK(wrch);
1699 } else
1700 ret = EINVAL;
1701 }
1702 break;
1703
1704 case SNDCTL_DSP_GETCAPS:
2a1ad637
FT
1705 PCM_LOCK(d);
1706 *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER;
558a398b 1707 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
2a1ad637
FT
1708 *arg_i |= PCM_CAP_DUPLEX;
1709 PCM_UNLOCK(d);
984263bc
MD
1710 break;
1711
1712 case SOUND_PCM_READ_BITS:
558a398b
SS
1713 chn = wrch ? wrch : rdch;
1714 if (chn) {
1715 CHN_LOCK(chn);
1716 if (chn->format & AFMT_8BIT)
1717 *arg_i = 8;
1718 else if (chn->format & AFMT_16BIT)
1719 *arg_i = 16;
1720 else if (chn->format & AFMT_24BIT)
1721 *arg_i = 24;
1722 else if (chn->format & AFMT_32BIT)
1723 *arg_i = 32;
1724 else
1725 ret = EINVAL;
1726 CHN_UNLOCK(chn);
1727 } else {
1728 *arg_i = 0;
1729 ret = EINVAL;
1730 }
984263bc
MD
1731 break;
1732
1733 case SNDCTL_DSP_SETTRIGGER:
1734 if (rdch) {
1735 CHN_LOCK(rdch);
2a1ad637 1736 rdch->flags &= ~CHN_F_NOTRIGGER;
984263bc
MD
1737 if (*arg_i & PCM_ENABLE_INPUT)
1738 chn_start(rdch, 1);
2a1ad637
FT
1739 else {
1740 chn_abort(rdch);
1741 chn_resetbuf(rdch);
984263bc 1742 rdch->flags |= CHN_F_NOTRIGGER;
2a1ad637 1743 }
984263bc
MD
1744 CHN_UNLOCK(rdch);
1745 }
1746 if (wrch) {
1747 CHN_LOCK(wrch);
2a1ad637 1748 wrch->flags &= ~CHN_F_NOTRIGGER;
984263bc
MD
1749 if (*arg_i & PCM_ENABLE_OUTPUT)
1750 chn_start(wrch, 1);
2a1ad637
FT
1751 else {
1752 chn_abort(wrch);
1753 chn_resetbuf(wrch);
984263bc 1754 wrch->flags |= CHN_F_NOTRIGGER;
2a1ad637 1755 }
558a398b 1756 CHN_UNLOCK(wrch);
984263bc
MD
1757 }
1758 break;
1759
1760 case SNDCTL_DSP_GETTRIGGER:
1761 *arg_i = 0;
558a398b
SS
1762 if (wrch) {
1763 CHN_LOCK(wrch);
1764 if (wrch->flags & CHN_F_TRIGGERED)
1765 *arg_i |= PCM_ENABLE_OUTPUT;
1766 CHN_UNLOCK(wrch);
1767 }
1768 if (rdch) {
1769 CHN_LOCK(rdch);
1770 if (rdch->flags & CHN_F_TRIGGERED)
1771 *arg_i |= PCM_ENABLE_INPUT;
1772 CHN_UNLOCK(rdch);
1773 }
984263bc
MD
1774 break;
1775
1776 case SNDCTL_DSP_GETODELAY:
1777 if (wrch) {
984263bc
MD
1778 struct snd_dbuf *bs = wrch->bufsoft;
1779
1780 CHN_LOCK(wrch);
558a398b 1781 /* XXX abusive DMA update: chn_wrupdate(wrch); */
2a1ad637 1782 *arg_i = sndbuf_getready(bs);
984263bc
MD
1783 CHN_UNLOCK(wrch);
1784 } else
1785 ret = EINVAL;
1786 break;
1787
1788 case SNDCTL_DSP_POST:
1789 if (wrch) {
1790 CHN_LOCK(wrch);
1791 wrch->flags &= ~CHN_F_NOTRIGGER;
1792 chn_start(wrch, 1);
1793 CHN_UNLOCK(wrch);
1794 }
1795 break;
1796
558a398b
SS
1797 case SNDCTL_DSP_SETDUPLEX:
1798 /*
1799 * switch to full-duplex mode if card is in half-duplex
1800 * mode and is able to work in full-duplex mode
1801 */
2a1ad637 1802 PCM_LOCK(d);
558a398b
SS
1803 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1804 dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
2a1ad637
FT
1805 PCM_UNLOCK(d);
1806 break;
1807
1808 /*
1809 * The following four ioctls are simple wrappers around mixer_ioctl
1810 * with no further processing. xcmd is short for "translated
1811 * command".
1812 */
1813 case SNDCTL_DSP_GETRECVOL:
1814 if (xcmd == 0) {
1815 xcmd = SOUND_MIXER_READ_RECLEV;
1816 chn = rdch;
1817 }
1818 /* FALLTHROUGH */
1819 case SNDCTL_DSP_SETRECVOL:
1820 if (xcmd == 0) {
1821 xcmd = SOUND_MIXER_WRITE_RECLEV;
1822 chn = rdch;
1823 }
1824 /* FALLTHROUGH */
1825 case SNDCTL_DSP_GETPLAYVOL:
1826 if (xcmd == 0) {
1827 xcmd = SOUND_MIXER_READ_PCM;
1828 chn = wrch;
1829 }
1830 /* FALLTHROUGH */
1831 case SNDCTL_DSP_SETPLAYVOL:
1832 if (xcmd == 0) {
1833 xcmd = SOUND_MIXER_WRITE_PCM;
1834 chn = wrch;
1835 }
1836
1837 ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg);
1838 if (ret != -1) {
1839 PCM_GIANT_EXIT(d);
1840 return (ret);
1841 }
1842
1843 if (d->mixer_dev != NULL) {
1844 PCM_ACQUIRE_QUICK(d);
a9dbef8b 1845 ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1,
2a1ad637
FT
1846 MIXER_CMD_DIRECT);
1847 PCM_RELEASE_QUICK(d);
1848 } else
1849 ret = ENOTSUP;
1850
1851 break;
1852
1853 case SNDCTL_DSP_GET_RECSRC_NAMES:
1854 case SNDCTL_DSP_GET_RECSRC:
1855 case SNDCTL_DSP_SET_RECSRC:
1856 if (d->mixer_dev != NULL) {
1857 PCM_ACQUIRE_QUICK(d);
a9dbef8b 1858 ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1,
2a1ad637
FT
1859 MIXER_CMD_DIRECT);
1860 PCM_RELEASE_QUICK(d);
1861 } else
1862 ret = ENOTSUP;
1863 break;
1864
1865 /*
1866 * The following 3 ioctls aren't very useful at the moment. For
1867 * now, only a single channel is associated with a cdev (/dev/dspN
1868 * instance), so there's only a single output routing to use (i.e.,
1869 * the wrch bound to this cdev).
1870 */
1871 case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1872 {
1873 oss_mixer_enuminfo *ei;
1874 ei = (oss_mixer_enuminfo *)arg;
1875 ei->dev = 0;
1876 ei->ctrl = 0;
1877 ei->version = 0; /* static for now */
1878 ei->strindex[0] = 0;
1879
1880 if (wrch != NULL) {
1881 ei->nvalues = 1;
1882 strlcpy(ei->strings, wrch->name,
1883 sizeof(ei->strings));
1884 } else {
1885 ei->nvalues = 0;
1886 ei->strings[0] = '\0';
1887 }
1888 }
1889 break;
1890 case SNDCTL_DSP_GET_PLAYTGT:
1891 case SNDCTL_DSP_SET_PLAYTGT: /* yes, they are the same for now */
1892 /*
1893 * Re: SET_PLAYTGT
1894 * OSSv4: "The value that was accepted by the device will
1895 * be returned back in the variable pointed by the
1896 * argument."
1897 */
1898 if (wrch != NULL)
1899 *arg_i = 0;
1900 else
1901 ret = EINVAL;
1902 break;
1903
1904 case SNDCTL_DSP_SILENCE:
1905 /*
1906 * Flush the software (pre-feed) buffer, but try to minimize playback
1907 * interruption. (I.e., record unplayed samples with intent to
1908 * restore by SNDCTL_DSP_SKIP.) Intended for application "pause"
1909 * functionality.
1910 */
1911 if (wrch == NULL)
1912 ret = EINVAL;
1913 else {
1914 struct snd_dbuf *bs;
1915 CHN_LOCK(wrch);
1916 while (wrch->inprog != 0)
1917 cv_wait(&wrch->cv, wrch->lock);
1918 bs = wrch->bufsoft;
1919 if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) {
1920 bs->sl = sndbuf_getready(bs);
1921 sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs));
1922 sndbuf_fillsilence(bs);
1923 chn_start(wrch, 0);
1924 }
1925 CHN_UNLOCK(wrch);
1926 }
1927 break;
1928
1929 case SNDCTL_DSP_SKIP:
1930 /*
1931 * OSSv4 docs: "This ioctl call discards all unplayed samples in the
1932 * playback buffer by moving the current write position immediately
1933 * before the point where the device is currently reading the samples."
1934 */
1935 if (wrch == NULL)
1936 ret = EINVAL;
1937 else {
1938 struct snd_dbuf *bs;
1939 CHN_LOCK(wrch);
1940 while (wrch->inprog != 0)
1941 cv_wait(&wrch->cv, wrch->lock);
1942 bs = wrch->bufsoft;
1943 if ((bs->shadbuf != NULL) && (bs->sl > 0)) {
1944 sndbuf_softreset(bs);
1945 sndbuf_acquire(bs, bs->shadbuf, bs->sl);
1946 bs->sl = 0;
1947 chn_start(wrch, 0);
1948 }
1949 CHN_UNLOCK(wrch);
1950 }
1951 break;
1952
1953 case SNDCTL_DSP_CURRENT_OPTR:
1954 case SNDCTL_DSP_CURRENT_IPTR:
1955 /**
1956 * @note Changing formats resets the buffer counters, which differs
1957 * from the 4Front drivers. However, I don't expect this to be
1958 * much of a problem.
1959 *
1960 * @note In a test where @c CURRENT_OPTR is called immediately after write
1961 * returns, this driver is about 32K samples behind whereas
1962 * 4Front's is about 8K samples behind. Should determine source
1963 * of discrepancy, even if only out of curiosity.
1964 *
1965 * @todo Actually test SNDCTL_DSP_CURRENT_IPTR.
1966 */
1967 chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch;
1968 if (chn == NULL)
1969 ret = EINVAL;
1970 else {
1971 struct snd_dbuf *bs;
1972 /* int tmp; */
1973
1974 oss_count_t *oc = (oss_count_t *)arg;
1975
1976 CHN_LOCK(chn);
1977 bs = chn->bufsoft;
1978#if 0
1979 tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b);
1980 oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getalign(b);
1981 oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getalign(b);
1982#else
1983 oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs);
1984 oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs);
1985#endif
1986 CHN_UNLOCK(chn);
1987 }
1988 break;
1989
1990 case SNDCTL_DSP_HALT_OUTPUT:
1991 case SNDCTL_DSP_HALT_INPUT:
1992 chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch;
1993 if (chn == NULL)
1994 ret = EINVAL;
1995 else {
1996 CHN_LOCK(chn);
1997 chn_abort(chn);
1998 CHN_UNLOCK(chn);
1999 }
2000 break;
2001
2002 case SNDCTL_DSP_LOW_WATER:
2003 /*
2004 * Set the number of bytes required to attract attention by
2005 * select/poll.
2006 */
2007 if (wrch != NULL) {
2008 CHN_LOCK(wrch);
2009 wrch->lw = (*arg_i > 1) ? *arg_i : 1;
2010 CHN_UNLOCK(wrch);
2011 }
2012 if (rdch != NULL) {
2013 CHN_LOCK(rdch);
2014 rdch->lw = (*arg_i > 1) ? *arg_i : 1;
2015 CHN_UNLOCK(rdch);
2016 }
2017 break;
2018
2019 case SNDCTL_DSP_GETERROR:
2020 /*
2021 * OSSv4 docs: "All errors and counters will automatically be
2022 * cleared to zeroes after the call so each call will return only
2023 * the errors that occurred after the previous invocation. ... The
2024 * play_underruns and rec_overrun fields are the only useful fields
2025 * returned by OSS 4.0."
2026 */
2027 {
2028 audio_errinfo *ei = (audio_errinfo *)arg;
2029
2030 bzero((void *)ei, sizeof(*ei));
2031
2032 if (wrch != NULL) {
2033 CHN_LOCK(wrch);
2034 ei->play_underruns = wrch->xruns;
2035 wrch->xruns = 0;
2036 CHN_UNLOCK(wrch);
2037 }
2038 if (rdch != NULL) {
2039 CHN_LOCK(rdch);
2040 ei->rec_overruns = rdch->xruns;
2041 rdch->xruns = 0;
2042 CHN_UNLOCK(rdch);
2043 }
2044 }
2045 break;
2046
2047 case SNDCTL_DSP_SYNCGROUP:
2048 PCM_ACQUIRE_QUICK(d);
2049 ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg);
2050 PCM_RELEASE_QUICK(d);
2051 break;
2052
2053 case SNDCTL_DSP_SYNCSTART:
2054 PCM_ACQUIRE_QUICK(d);
2055 ret = dsp_oss_syncstart(*arg_i);
2056 PCM_RELEASE_QUICK(d);
2057 break;
2058
2059 case SNDCTL_DSP_POLICY:
2060 PCM_ACQUIRE_QUICK(d);
2061 ret = dsp_oss_policy(wrch, rdch, *arg_i);
2062 PCM_RELEASE_QUICK(d);
2063 break;
2064
2065 case SNDCTL_DSP_COOKEDMODE:
2066 PCM_ACQUIRE_QUICK(d);
2067 if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT))
2068 ret = dsp_oss_cookedmode(wrch, rdch, *arg_i);
2069 PCM_RELEASE_QUICK(d);
2070 break;
2071 case SNDCTL_DSP_GET_CHNORDER:
2072 PCM_ACQUIRE_QUICK(d);
2073 ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg);
2074 PCM_RELEASE_QUICK(d);
2075 break;
2076 case SNDCTL_DSP_SET_CHNORDER:
2077 PCM_ACQUIRE_QUICK(d);
2078 ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg);
2079 PCM_RELEASE_QUICK(d);
2080 break;
2081 case SNDCTL_DSP_GETCHANNELMASK: /* XXX vlc */
2082 PCM_ACQUIRE_QUICK(d);
2083 ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg);
2084 PCM_RELEASE_QUICK(d);
2085 break;
2086 case SNDCTL_DSP_BIND_CHANNEL: /* XXX what?!? */
2087 ret = EINVAL;
2088 break;
2089#ifdef OSSV4_EXPERIMENT
2090 /*
2091 * XXX The following ioctls are not yet supported and just return
2092 * EINVAL.
2093 */
2094 case SNDCTL_DSP_GETOPEAKS:
2095 case SNDCTL_DSP_GETIPEAKS:
2096 chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch;
2097 if (chn == NULL)
2098 ret = EINVAL;
2099 else {
2100 oss_peaks_t *op = (oss_peaks_t *)arg;
2101 int lpeak, rpeak;
2102
2103 CHN_LOCK(chn);
2104 ret = chn_getpeaks(chn, &lpeak, &rpeak);
2105 if (ret == -1)
2106 ret = EINVAL;
2107 else {
2108 (*op)[0] = lpeak;
2109 (*op)[1] = rpeak;
2110 }
2111 CHN_UNLOCK(chn);
2112 }
2113 break;
2114
2115 /*
2116 * XXX Once implemented, revisit this for proper cv protection
2117 * (if necessary).
2118 */
2119 case SNDCTL_GETLABEL:
2120 ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg);
2121 break;
2122 case SNDCTL_SETLABEL:
2123 ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg);
558a398b 2124 break;
2a1ad637
FT
2125 case SNDCTL_GETSONG:
2126 ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg);
2127 break;
2128 case SNDCTL_SETSONG:
2129 ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg);
2130 break;
2131 case SNDCTL_SETNAME:
2132 ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg);
2133 break;
2134#if 0
2135 /**
2136 * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and
2137 * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of
2138 * 4Front Technologies.
2139 */
2140 case SNDCTL_DSP_READCTL:
2141 case SNDCTL_DSP_WRITECTL:
2142 ret = EINVAL;
2143 break;
2144#endif /* !0 (explicitly omitted ioctls) */
558a398b 2145
2a1ad637 2146#endif /* !OSSV4_EXPERIMENT */
984263bc
MD
2147 case SNDCTL_DSP_MAPINBUF:
2148 case SNDCTL_DSP_MAPOUTBUF:
2149 case SNDCTL_DSP_SETSYNCRO:
2150 /* undocumented */
2151
2152 case SNDCTL_DSP_SUBDIVIDE:
2153 case SOUND_PCM_WRITE_FILTER:
2154 case SOUND_PCM_READ_FILTER:
2155 /* dunno what these do, don't sound important */
558a398b 2156
984263bc 2157 default:
67931cc4 2158 DEB(kprintf("default ioctl fn 0x%08lx fail\n", cmd));
984263bc
MD
2159 ret = EINVAL;
2160 break;
2161 }
984263bc 2162
2a1ad637
FT
2163 PCM_GIANT_LEAVE(d);
2164
2165 return (ret);
2166}
64ab07b8 2167
9bade76f
FT
2168static struct filterops dsp_read_filtops =
2169 { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_read };
2170static struct filterops dsp_write_filtops =
2171 { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_write };
2172
64ab07b8 2173static int
9bade76f
FT
2174/*dsp_poll(struct cdev *i_dev, int events, struct thread *td)*/
2175dsp_kqfilter(struct dev_kqfilter_args *ap)
64ab07b8 2176{
9bade76f
FT
2177 struct knote *kn = ap->a_kn;
2178 struct klist *klist;
2179 struct cdev *i_dev = ap->a_head.a_dev;
2a1ad637
FT
2180 struct snddev_info *d;
2181 struct pcm_channel *wrch, *rdch;
9bade76f
FT
2182 struct snd_dbuf *bs = NULL;
2183 int ret;
2a1ad637
FT
2184
2185 d = dsp_get_info(i_dev);
2186 if (!DSP_REGISTERED(d, i_dev))
2187 return (EBADF);
2188
2189 PCM_GIANT_ENTER(d);
2190
2191 wrch = NULL;
2192 rdch = NULL;
2193 ret = 0;
64ab07b8
SG
2194
2195 getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
b287d649 2196
9bade76f
FT
2197 switch (kn->kn_filter) {
2198 case EVFILT_READ:
2199 if (rdch) {
2200 kn->kn_fop = &dsp_read_filtops;
2201 kn->kn_hook = (caddr_t)rdch;
2202 bs = rdch->bufsoft;
2203 ap->a_result = 0;
2204 }
2205 break;
2206 case EVFILT_WRITE:
2207 if (wrch) {
2208 kn->kn_fop = &dsp_write_filtops;
2209 kn->kn_hook = (caddr_t)wrch;
2210 bs = wrch->bufsoft;
2211 ap->a_result = 0;
2212 }
2213 break;
2214 default:
2215 ap->a_result = EOPNOTSUPP;
2216 break;
64ab07b8
SG
2217 }
2218
9bade76f
FT
2219 if (ap->a_result == 0) {
2220 klist = &sndbuf_getkq(bs)->ki_note;
2221 knote_insert(klist, kn);
64ab07b8
SG
2222 }
2223
2224 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
2225
2a1ad637 2226 PCM_GIANT_LEAVE(d);
64ab07b8 2227
2a1ad637 2228 return (ret);
64ab07b8
SG
2229}
2230
9bade76f
FT
2231static void
2232dsp_filter_detach(struct knote *kn)
2233{
2234 struct pcm_channel *ch = (struct pcm_channel *)kn->kn_hook;
2235 struct snd_dbuf *bs = ch->bufsoft;
2236 struct klist *klist;
2237
2238 CHN_LOCK(ch);
2239 klist = &sndbuf_getkq(bs)->ki_note;
2240 knote_remove(klist, kn);
2241 CHN_UNLOCK(ch);
2242}
2243
2244static int
2245dsp_filter_read(struct knote *kn, long hint)
2246{
2247 struct pcm_channel *rdch = (struct pcm_channel *)kn->kn_hook;
2248 struct thread *td = curthread;
2249 int ready;
2250
2251 CHN_LOCK(rdch);
2252 ready = chn_poll(rdch, 1, td);
2253 CHN_UNLOCK(rdch);
2254
2255 return (ready);
2256}
2257
2258static int
2259dsp_filter_write(struct knote *kn, long hint)
2260{
2261 struct pcm_channel *wrch = (struct pcm_channel *)kn->kn_hook;
2262 struct thread *td = curthread;
2263 int ready;
2264
2265 CHN_LOCK(wrch);
2266 ready = chn_poll(wrch, 1, td);
2267 CHN_UNLOCK(wrch);
2268
2269 return (ready);
2270}
2271
64ab07b8 2272static int
88a31edf 2273dsp_mmap(struct dev_mmap_args *ap)
64ab07b8 2274{
88a31edf 2275 vm_offset_t offset = ap->a_offset;
64ab07b8 2276
2a1ad637 2277 /* XXX memattr is not honored */
88a31edf 2278 ap->a_result = vtophys(offset);
2a1ad637 2279 return (0);
64ab07b8 2280}
984263bc
MD
2281
2282static int
88a31edf 2283dsp_mmap_single(struct dev_mmap_single_args *ap)
984263bc 2284{
88a31edf
FT
2285 struct cdev *i_dev = ap->a_head.a_dev;
2286 vm_ooffset_t *offset = ap->a_offset;
2287 vm_size_t size = ap->a_size;
2288 struct vm_object **object = ap->a_object;
2289 int nprot = ap->a_nprot;
2a1ad637
FT
2290 struct snddev_info *d;
2291 struct pcm_channel *wrch, *rdch, *c;
984263bc 2292
984263bc 2293 /*
2a1ad637
FT
2294 * Reject PROT_EXEC by default. It just doesn't makes sense.
2295 * Unfortunately, we have to give up this one due to linux_mmap
2296 * changes.
2297 *
2298 * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html
2299 *
984263bc 2300 */
2a1ad637
FT
2301#ifdef SV_ABI_LINUX
2302 if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 ||
2303 (dsp_mmap_allow_prot_exec == 0 &&
2304 SV_CURPROC_ABI() != SV_ABI_LINUX)))
984263bc 2305#else
2a1ad637 2306 if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1)
984263bc 2307#endif
2a1ad637 2308 return (EINVAL);
984263bc 2309
2a1ad637
FT
2310 /*
2311 * PROT_READ (alone) selects the input buffer.
2312 * PROT_WRITE (alone) selects the output buffer.
2313 * PROT_WRITE|PROT_READ together select the output buffer.
2314 */
2315 if ((nprot & (PROT_READ | PROT_WRITE)) == 0)
2316 return (EINVAL);
2317
2318 d = dsp_get_info(i_dev);
2319 if (!DSP_REGISTERED(d, i_dev))
2320 return (EINVAL);
2321
2322 PCM_GIANT_ENTER(d);
2323
2324 getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
984263bc 2325
2a1ad637
FT
2326 c = ((nprot & PROT_WRITE) != 0) ? wrch : rdch;
2327 if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) ||
2328 (*offset + size) > sndbuf_getsize(c->bufsoft) ||
2329 (wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) ||
2330 (rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) {
558a398b 2331 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
2a1ad637
FT
2332 PCM_GIANT_EXIT(d);
2333 return (EINVAL);
984263bc
MD
2334 }
2335
2a1ad637
FT
2336 if (wrch != NULL)
2337 wrch->flags |= CHN_F_MMAP;
2338 if (rdch != NULL)
2339 rdch->flags |= CHN_F_MMAP;
984263bc 2340
2a1ad637 2341 *offset = (uintptr_t)sndbuf_getbufofs(c->bufsoft, *offset);
558a398b 2342 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
67931cc4 2343 *object = dev_pager_alloc(i_dev, size, nprot, *offset);
984263bc 2344
2a1ad637
FT
2345 PCM_GIANT_LEAVE(d);
2346
2347 if (*object == NULL)
2348 return (EINVAL);
558a398b 2349 return (0);
984263bc
MD
2350}
2351
2a1ad637
FT
2352/* So much for dev_stdclone() */
2353static int
67931cc4 2354dsp_stdclone(const char *name, char *namep, char *sep, int use_sep, int *u, int *c)
984263bc 2355{
2a1ad637 2356 size_t len;
984263bc 2357
2a1ad637 2358 len = strlen(namep);
558a398b 2359
2a1ad637 2360 if (bcmp(name, namep, len) != 0)
af8f7a68
SS
2361 return (ENODEV);
2362
2a1ad637 2363 name += len;
558a398b 2364
2a1ad637
FT
2365 if (isdigit(*name) == 0)
2366 return (ENODEV);
558a398b 2367
2a1ad637 2368 len = strlen(sep);
558a398b 2369
2a1ad637
FT
2370 if (*name == '0' && !(name[1] == '\0' || bcmp(name + 1, sep, len) == 0))
2371 return (ENODEV);
2372
2373 for (*u = 0; isdigit(*name) != 0; name++) {
2374 *u *= 10;
2375 *u += *name - '0';
2376 if (*u > dsp_umax)
2377 return (ENODEV);
984263bc 2378 }
984263bc 2379
2a1ad637
FT
2380 if (*name == '\0')
2381 return ((use_sep == 0) ? 0 : ENODEV);
2382
2383 if (bcmp(name, sep, len) != 0 || isdigit(name[len]) == 0)
2384 return (ENODEV);
2385
2386 name += len;
2387
2388 if (*name == '0' && name[1] != '\0')
2389 return (ENODEV);
2390
2391 for (*c = 0; isdigit(*name) != 0; name++) {
2392 *c *= 10;
2393 *c += *name - '0';
2394 if (*c > dsp_cmax)
2395 return (ENODEV);
af8f7a68 2396 }
984263bc 2397
2a1ad637
FT
2398 if (*name != '\0')
2399 return (ENODEV);
2400
2401 return (0);
2402}
2403
59b2ebda
FT
2404/*
2405 * for i = 0 to channels of device N
2406 * if dspN.i isn't busy and in the right dir, create a dev_t and return it
2407 */
18099ae9
FT
2408int
2409dsp_clone(struct dev_clone_args *ap)
2a1ad637 2410{
18099ae9
FT
2411 struct cdev *i_dev = ap->a_head.a_dev;
2412 const char *name = ap->a_name;
2a1ad637
FT
2413 struct snddev_info *d;
2414 struct snd_clone_entry *ce;
2415 struct pcm_channel *c;
2416 int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax;
2417 char *devname, *devcmp, *devsep;
18099ae9
FT
2418 int err = EBUSY;
2419 static struct cdev *dev;
2a1ad637
FT
2420
2421 KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!"));
2422
18099ae9
FT
2423 d = dsp_get_info(i_dev);
2424 if (d != NULL) {
2425 return (ENODEV);
2426 }
2a1ad637
FT
2427
2428 unit = -1;
2429 cunit = -1;
2430 devtype = -1;
2431 devhw = 0;
2432 devcmax = -1;
2433 tumax = -1;
2434 devname = NULL;
2435 devsep = NULL;
2436
2437 for (i = 0; unit == -1 &&
2438 i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
2439 devtype = dsp_cdevs[i].type;
2440 devcmp = dsp_cdevs[i].name;
2441 devsep = dsp_cdevs[i].sep;
2442 devname = dsp_cdevs[i].alias;
2443 if (devname == NULL)
2444 devname = devcmp;
2445 devhw = dsp_cdevs[i].hw;
2446 devcmax = dsp_cdevs[i].max - 1;
2447 if (strcmp(name, devcmp) == 0)
2448 unit = snd_unit;
2449 else if (dsp_stdclone(name, devcmp, devsep,
2450 dsp_cdevs[i].use_sep, &unit, &cunit) != 0) {
2451 unit = -1;
2452 cunit = -1;
2453 }
2454 }
2455
2456 d = devclass_get_softc(pcm_devclass, unit);
18099ae9
FT
2457 if (!PCM_REGISTERED(d) || d->clones == NULL) {
2458 return (ENODEV);
2459 }
2a1ad637
FT
2460
2461 /* XXX Need Giant magic entry ??? */
2462
2463 PCM_LOCK(d);
2464 if (snd_clone_disabled(d->clones)) {
2465 PCM_UNLOCK(d);
18099ae9 2466 return (ENODEV);
2a1ad637
FT
2467 }
2468
2469 PCM_WAIT(d);
2470 PCM_ACQUIRE(d);
2471 PCM_UNLOCK(d);
2472
2473 udcmask = snd_u2unit(unit) | snd_d2unit(devtype);
2474
2475 if (devhw != 0) {
2476 KASSERT(devcmax <= dsp_cmax,
2477 ("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax));
2478 if (cunit > devcmax) {
2479 PCM_RELEASE_QUICK(d);
18099ae9 2480 return (ENODEV);
2a1ad637
FT
2481 }
2482 udcmask |= snd_c2unit(cunit);
2483 CHN_FOREACH(c, d, channels.pcm) {
2484 CHN_LOCK(c);
2485 if (c->unit != udcmask) {
2486 CHN_UNLOCK(c);
2487 continue;
2488 }
2489 CHN_UNLOCK(c);
2490 udcmask &= ~snd_c2unit(cunit);
2491 /*
2492 * Temporarily increase clone maxunit to overcome
2493 * vchan flexibility.
2494 *
2495 * # sysctl dev.pcm.0.play.vchans=256
2496 * dev.pcm.0.play.vchans: 1 -> 256
2497 * # cat /dev/zero > /dev/dsp0.vp255 &
2498 * [1] 17296
2499 * # sysctl dev.pcm.0.play.vchans=0
2500 * dev.pcm.0.play.vchans: 256 -> 1
2501 * # fg
2502 * [1] + running cat /dev/zero > /dev/dsp0.vp255
2503 * ^C
2504 * # cat /dev/zero > /dev/dsp0.vp255
2505 * zsh: operation not supported: /dev/dsp0.vp255
2506 */
2507 tumax = snd_clone_getmaxunit(d->clones);
2508 if (cunit > tumax)
2509 snd_clone_setmaxunit(d->clones, cunit);
2510 else
2511 tumax = -1;
2512 goto dsp_clone_alloc;
2513 }
2514 /*
2515 * Ok, so we're requesting unallocated vchan, but still
2516 * within maximum vchan limit.
2517 */
2518 if (((devtype == SND_DEV_DSPHW_VPLAY && d->pvchancount > 0) ||
2519 (devtype == SND_DEV_DSPHW_VREC && d->rvchancount > 0)) &&
2520 cunit < snd_maxautovchans) {
2521 udcmask &= ~snd_c2unit(cunit);
2522 tumax = snd_clone_getmaxunit(d->clones);
2523 if (cunit > tumax)
2524 snd_clone_setmaxunit(d->clones, cunit);
2525 else
2526 tumax = -1;
2527 goto dsp_clone_alloc;
2528 }
2529 PCM_RELEASE_QUICK(d);
18099ae9 2530 return (err);
2a1ad637
FT
2531 }
2532
2533dsp_clone_alloc:
18099ae9 2534 ce = snd_clone_alloc(d->clones, &dev, &cunit, udcmask);
2a1ad637
FT
2535 if (tumax != -1)
2536 snd_clone_setmaxunit(d->clones, tumax);
2537 if (ce != NULL) {
2538 udcmask |= snd_c2unit(cunit);
88a31edf 2539 dev = make_only_dev(&dsp_ops, PCMMINOR(udcmask),
2a1ad637
FT
2540 UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d",
2541 devname, unit, devsep, cunit);
18099ae9
FT
2542 snd_clone_register(ce, dev);
2543 err = 0;
2a1ad637
FT
2544 }
2545
2546 PCM_RELEASE_QUICK(d);
67931cc4 2547#if 0
2a1ad637
FT
2548 if (*dev != NULL)
2549 dev_ref(*dev);
67931cc4 2550#endif
18099ae9 2551 return (err);
2a1ad637
FT
2552}
2553
2554static void
2555dsp_sysinit(void *p)
2556{
2557 if (dsp_ehtag != NULL)
2558 return;
2559 /* initialize unit numbering */
2560 snd_unit_init();
2561 dsp_umax = PCMMAXUNIT;
2562 dsp_cmax = PCMMAXCHAN;
2563 dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
2564}
2565
2566static void
2567dsp_sysuninit(void *p)
2568{
2569 if (dsp_ehtag == NULL)
2570 return;
2571 EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
2572 dsp_ehtag = NULL;
2573}
2574
2575SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
2576SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
2577
2578char *
2579dsp_unit2name(char *buf, size_t len, int unit)
2580{
2581 int i, dtype;
2582
2583 KASSERT(buf != NULL && len != 0,
2584 ("bogus buf=%p len=%ju", buf, (uintmax_t)len));
2585
2586 dtype = snd_unit2d(unit);
2587
2588 for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
2589 if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL)
2590 continue;
67931cc4 2591 ksnprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name,
2a1ad637
FT
2592 snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit));
2593 return (buf);
2594 }
2595
2596 return (NULL);
2597}
2598
2599/**
2600 * @brief Handler for SNDCTL_AUDIOINFO.
2601 *
2602 * Gathers information about the audio device specified in ai->dev. If
2603 * ai->dev == -1, then this function gathers information about the current
2604 * device. If the call comes in on a non-audio device and ai->dev == -1,
2605 * return EINVAL.
2606 *
2607 * This routine is supposed to go practically straight to the hardware,
2608 * getting capabilities directly from the sound card driver, side-stepping
2609 * the intermediate channel interface.
2610 *
2611 * Note, however, that the usefulness of this command is significantly
2612 * decreased when requesting info about any device other than the one serving
2613 * the request. While each snddev_channel refers to a specific device node,
2614 * the converse is *not* true. Currently, when a sound device node is opened,
2615 * the sound subsystem scans for an available audio channel (or channels, if
2616 * opened in read+write) and then assigns them to the si_drv[12] private
2617 * data fields. As a result, any information returned linking a channel to
2618 * a specific character device isn't necessarily accurate.
2619 *
2620 * @note
2621 * Calling threads must not hold any snddev_info or pcm_channel locks.
2622 *
2623 * @param dev device on which the ioctl was issued
2624 * @param ai ioctl request data container
2625 *
2626 * @retval 0 success
2627 * @retval EINVAL ai->dev specifies an invalid device
2628 *
2629 * @todo Verify correctness of Doxygen tags. ;)
2630 */
2631int
2632dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
2633{
2634 struct pcmchan_caps *caps;
2635 struct pcm_channel *ch;
2636 struct snddev_info *d;
2637 uint32_t fmts;
2638 int i, nchan, *rates, minch, maxch;
2639 char *devname, buf[CHN_NAMELEN];
2640
2641 /*
2642 * If probing the device that received the ioctl, make sure it's a
2643 * DSP device. (Users may use this ioctl with /dev/mixer and
2644 * /dev/midi.)
2645 */
88a31edf 2646 if (ai->dev == -1 && i_dev->si_ops != &dsp_ops)
2a1ad637
FT
2647 return (EINVAL);
2648
2649 ch = NULL;
2650 devname = NULL;
2651 nchan = 0;
2652 bzero(buf, sizeof(buf));
2653
2654 /*
2655 * Search for the requested audio device (channel). Start by
2656 * iterating over pcm devices.
2657 */
2658 for (i = 0; pcm_devclass != NULL &&
2659 i < devclass_get_maxunit(pcm_devclass); i++) {
2660 d = devclass_get_softc(pcm_devclass, i);
2661 if (!PCM_REGISTERED(d))
2662 continue;
2663
2664 /* XXX Need Giant magic entry ??? */
2665
2666 /* See the note in function docblock */
2667 PCM_UNLOCKASSERT(d);
2668 PCM_LOCK(d);
2669
2670 CHN_FOREACH(ch, d, channels.pcm) {
2671 CHN_UNLOCKASSERT(ch);
2672 CHN_LOCK(ch);
2673 if (ai->dev == -1) {
2674 if (DSP_REGISTERED(d, i_dev) &&
2675 (ch == PCM_RDCH(i_dev) || /* record ch */
2676 ch == PCM_WRCH(i_dev))) { /* playback ch */
2677 devname = dsp_unit2name(buf,
2678 sizeof(buf), ch->unit);
2679 }
2680 } else if (ai->dev == nchan) {
2681 devname = dsp_unit2name(buf, sizeof(buf),
2682 ch->unit);
2683 }
2684 if (devname != NULL)
2685 break;
2686 CHN_UNLOCK(ch);
2687 ++nchan;
2688 }
2689
2690 if (devname != NULL) {
2691 /*
2692 * At this point, the following synchronization stuff
2693 * has happened:
2694 * - a specific PCM device is locked.
2695 * - a specific audio channel has been locked, so be
2696 * sure to unlock when exiting;
2697 */
2698
2699 caps = chn_getcaps(ch);
2700
2701 /*
2702 * With all handles collected, zero out the user's
2703 * container and begin filling in its fields.
2704 */
2705 bzero((void *)ai, sizeof(oss_audioinfo));
2706
2707 ai->dev = nchan;
2708 strlcpy(ai->name, ch->name, sizeof(ai->name));
2709
2710 if ((ch->flags & CHN_F_BUSY) == 0)
2711 ai->busy = 0;
2712 else
2713 ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
2714
2715 /**
2716 * @note
2717 * @c cmd - OSSv4 docs: "Only supported under Linux at
2718 * this moment." Cop-out, I know, but I'll save
2719 * running around in the process table for later.
2720 * Is there a risk of leaking information?
2721 */
2722 ai->pid = ch->pid;
2723
2724 /*
2725 * These flags stolen from SNDCTL_DSP_GETCAPS handler.
2726 * Note, however, that a single channel operates in
2727 * only one direction, so PCM_CAP_DUPLEX is out.
2728 */
2729 /**
2730 * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep
2731 * these in pcmchan::caps?
2732 */
2733 ai->caps = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER |
2734 ((ch->direction == PCMDIR_PLAY) ? PCM_CAP_OUTPUT : PCM_CAP_INPUT);
2735
2736 /*
2737 * Collect formats supported @b natively by the
2738 * device. Also determine min/max channels. (I.e.,
2739 * mono, stereo, or both?)
2740 *
2741 * If any channel is stereo, maxch = 2;
2742 * if all channels are stereo, minch = 2, too;
2743 * if any channel is mono, minch = 1;
2744 * and if all channels are mono, maxch = 1.
2745 */
2746 minch = 0;
2747 maxch = 0;
2748 fmts = 0;
2749 for (i = 0; caps->fmtlist[i]; i++) {
2750 fmts |= caps->fmtlist[i];
2751 if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) {
2752 minch = (minch == 0) ? 2 : minch;
2753 maxch = 2;
2754 } else {
2755 minch = 1;
2756 maxch = (maxch == 0) ? 1 : maxch;
2757 }
2758 }
2759
2760 if (ch->direction == PCMDIR_PLAY)
2761 ai->oformats = fmts;
2762 else
2763 ai->iformats = fmts;
2764
2765 /**
2766 * @note
2767 * @c magic - OSSv4 docs: "Reserved for internal use
2768 * by OSS."
2769 *
2770 * @par
2771 * @c card_number - OSSv4 docs: "Number of the sound
2772 * card where this device belongs or -1 if this
2773 * information is not available. Applications
2774 * should normally not use this field for any
2775 * purpose."
2776 */
2777 ai->card_number = -1;
2778 /**
2779 * @todo @c song_name - depends first on
2780 * SNDCTL_[GS]ETSONG @todo @c label - depends
2781 * on SNDCTL_[GS]ETLABEL
2782 * @todo @c port_number - routing information?
2783 */
2784 ai->port_number = -1;
2785 ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
2786 /**
2787 * @note
2788 * @c real_device - OSSv4 docs: "Obsolete."
2789 */
2790 ai->real_device = -1;
2791 strlcpy(ai->devnode, "/dev/", sizeof(ai->devnode));
2792 strlcat(ai->devnode, devname, sizeof(ai->devnode));
2793 ai->enabled = device_is_attached(d->dev) ? 1 : 0;
2794 /**
2795 * @note
2796 * @c flags - OSSv4 docs: "Reserved for future use."
2797 *
2798 * @note
2799 * @c binding - OSSv4 docs: "Reserved for future use."
2800 *
2801 * @todo @c handle - haven't decided how to generate
2802 * this yet; bus, vendor, device IDs?
2803 */
2804 ai->min_rate = caps->minspeed;
2805 ai->max_rate = caps->maxspeed;
2806
2807 ai->min_channels = minch;
2808 ai->max_channels = maxch;
2809
2810 ai->nrates = chn_getrates(ch, &rates);
2811 if (ai->nrates > OSS_MAX_SAMPLE_RATES)
2812 ai->nrates = OSS_MAX_SAMPLE_RATES;
2813
2814 for (i = 0; i < ai->nrates; i++)
2815 ai->rates[i] = rates[i];
2816
2817 ai->next_play_engine = 0;
2818 ai->next_rec_engine = 0;
2819
2820 CHN_UNLOCK(ch);
2821 }
2822
2823 PCM_UNLOCK(d);
2824
2825 if (devname != NULL)
2826 return (0);
2827 }
2828
2829 /* Exhausted the search -- nothing is locked, so return. */
2830 return (EINVAL);
2831}
2832
2833/**
2834 * @brief Assigns a PCM channel to a sync group.
2835 *
2836 * Sync groups are used to enable audio operations on multiple devices
2837 * simultaneously. They may be used with any number of devices and may
2838 * span across applications. Devices are added to groups with
2839 * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the
2840 * SNDCTL_DSP_SYNCSTART ioctl.
2841 *
2842 * If the @c id field of the @c group parameter is set to zero, then a new
2843 * sync group is created. Otherwise, wrch and rdch (if set) are added to
2844 * the group specified.
2845 *
2846 * @todo As far as memory allocation, should we assume that things are
2847 * okay and allocate with M_WAITOK before acquiring channel locks,
2848 * freeing later if not?
2849 *
2850 * @param wrch output channel associated w/ device (if any)
2851 * @param rdch input channel associated w/ device (if any)
2852 * @param group Sync group parameters
2853 *
2854 * @retval 0 success
2855 * @retval non-zero error to be propagated upstream
2856 */
2857static int
2858dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group)
2859{
2860 struct pcmchan_syncmember *smrd, *smwr;
2861 struct pcmchan_syncgroup *sg;
2862 int ret, sg_ids[3];
2863
2864 smrd = NULL;
2865 smwr = NULL;
2866 sg = NULL;
2867 ret = 0;
2868
2869 /*
2870 * Free_unr() may sleep, so store released syncgroup IDs until after
2871 * all locks are released.
2872 */
2873 sg_ids[0] = sg_ids[1] = sg_ids[2] = 0;
2874
2875 PCM_SG_LOCK();
2876
2877 /*
2878 * - Insert channel(s) into group's member list.
2879 * - Set CHN_F_NOTRIGGER on channel(s).
2880 * - Stop channel(s).
2881 */
2882
2883 /*
2884 * If device's channels are already mapped to a group, unmap them.
2885 */
2886 if (wrch) {
2887 CHN_LOCK(wrch);
2888 sg_ids[0] = chn_syncdestroy(wrch);
2889 }
2890
2891 if (rdch) {
2892 CHN_LOCK(rdch);
2893 sg_ids[1] = chn_syncdestroy(rdch);
2894 }
2895
2896 /*
2897 * Verify that mode matches character device properites.
2898 * - Bail if PCM_ENABLE_OUTPUT && wrch == NULL.
2899 * - Bail if PCM_ENABLE_INPUT && rdch == NULL.
2900 */
2901 if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) ||
2902 ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) {
2903 ret = EINVAL;
2904 goto out;
2905 }
2906
2907 /*
2908 * An id of zero indicates the user wants to create a new
2909 * syncgroup.
2910 */
2911 if (group->id == 0) {
67931cc4 2912 sg = (struct pcmchan_syncgroup *)kmalloc(sizeof(*sg), M_DEVBUF, M_NOWAIT);
2a1ad637
FT
2913 if (sg != NULL) {
2914 SLIST_INIT(&sg->members);
2915 sg->id = alloc_unr(pcmsg_unrhdr);
2916
2917 group->id = sg->id;
2918 SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link);
2919 } else
2920 ret = ENOMEM;
2921 } else {
2922 SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
2923 if (sg->id == group->id)
2924 break;
2925 }
2926 if (sg == NULL)
2927 ret = EINVAL;
2928 }
2929
2930 /* Couldn't create or find a syncgroup. Fail. */
2931 if (sg == NULL)
2932 goto out;
2933
2934 /*
2935 * Allocate a syncmember, assign it and a channel together, and
2936 * insert into syncgroup.
2937 */
2938 if (group->mode & PCM_ENABLE_INPUT) {
67931cc4 2939 smrd = (struct pcmchan_syncmember *)kmalloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
2a1ad637
FT
2940 if (smrd == NULL) {
2941 ret = ENOMEM;
2942 goto out;
2943 }
2944
2945 SLIST_INSERT_HEAD(&sg->members, smrd, link);
2946 smrd->parent = sg;
2947 smrd->ch = rdch;
2948
2949 chn_abort(rdch);
2950 rdch->flags |= CHN_F_NOTRIGGER;
2951 rdch->sm = smrd;
2952 }
2953
2954 if (group->mode & PCM_ENABLE_OUTPUT) {
67931cc4 2955 smwr = (struct pcmchan_syncmember *)kmalloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT);
2a1ad637
FT
2956 if (smwr == NULL) {
2957 ret = ENOMEM;
2958 goto out;
2959 }
2960
2961 SLIST_INSERT_HEAD(&sg->members, smwr, link);
2962 smwr->parent = sg;
2963 smwr->ch = wrch;
2964
2965 chn_abort(wrch);
2966 wrch->flags |= CHN_F_NOTRIGGER;
2967 wrch->sm = smwr;
2968 }
2969
2970
2971out:
2972 if (ret != 0) {
2973 if (smrd != NULL)
67931cc4 2974 kfree(smrd, M_DEVBUF);
2a1ad637
FT
2975 if ((sg != NULL) && SLIST_EMPTY(&sg->members)) {
2976 sg_ids[2] = sg->id;
2977 SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
67931cc4 2978 kfree(sg, M_DEVBUF);
2a1ad637
FT
2979 }
2980
2981 if (wrch)
2982 wrch->sm = NULL;
2983 if (rdch)
2984 rdch->sm = NULL;
2985 }
2986
2987 if (wrch)
2988 CHN_UNLOCK(wrch);
2989 if (rdch)
2990 CHN_UNLOCK(rdch);
2991
2992 PCM_SG_UNLOCK();
2993
2994 if (sg_ids[0])
2995 free_unr(pcmsg_unrhdr, sg_ids[0]);
2996 if (sg_ids[1])
2997 free_unr(pcmsg_unrhdr, sg_ids[1]);
2998 if (sg_ids[2])
2999 free_unr(pcmsg_unrhdr, sg_ids[2]);
3000
3001 return (ret);
3002}
3003
3004/**
3005 * @brief Launch a sync group into action
3006 *
3007 * Sync groups are established via SNDCTL_DSP_SYNCGROUP. This function
3008 * iterates over all members, triggering them along the way.
3009 *
3010 * @note Caller must not hold any channel locks.
3011 *
3012 * @param sg_id sync group identifier
3013 *
3014 * @retval 0 success
3015 * @retval non-zero error worthy of propagating upstream to user
3016 */
3017static int
3018dsp_oss_syncstart(int sg_id)
3019{
3020 struct pcmchan_syncmember *sm, *sm_tmp;
3021 struct pcmchan_syncgroup *sg;
3022 struct pcm_channel *c;
3023 int ret, needlocks;
3024
3025 /* Get the synclists lock */
3026 PCM_SG_LOCK();
3027
3028 do {
3029 ret = 0;
3030 needlocks = 0;
3031
3032 /* Search for syncgroup by ID */
3033 SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
3034 if (sg->id == sg_id)
3035 break;
3036 }
3037
3038 /* Return EINVAL if not found */
3039 if (sg == NULL) {
3040 ret = EINVAL;
3041 break;
3042 }
3043
3044 /* Any removals resulting in an empty group should've handled this */
3045 KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup"));
3046
3047 /*
3048 * Attempt to lock all member channels - if any are already
3049 * locked, unlock those acquired, sleep for a bit, and try
3050 * again.
3051 */
3052 SLIST_FOREACH(sm, &sg->members, link) {
3053 if (CHN_TRYLOCK(sm->ch) == 0) {
3054 int timo = hz * 5/1000;
3055 if (timo < 1)
3056 timo = 1;
3057
3058 /* Release all locked channels so far, retry */
3059 SLIST_FOREACH(sm_tmp, &sg->members, link) {
3060 /* sm is the member already locked */
3061 if (sm == sm_tmp)
3062 break;
3063 CHN_UNLOCK(sm_tmp->ch);
3064 }
3065
3066 /** @todo Is PRIBIO correct/ */
67931cc4
FT
3067 ret = lksleep(sm, &snd_pcm_syncgroups_mtx,
3068 PCATCH, "pcmsg", timo);
2a1ad637
FT
3069 if (ret == EINTR || ret == ERESTART)
3070 break;
3071
3072 needlocks = 1;
3073 ret = 0; /* Assumes ret == EAGAIN... */
3074 }
3075 }
3076 } while (needlocks && ret == 0);
3077
3078 /* Proceed only if no errors encountered. */
3079 if (ret == 0) {
3080 /* Launch channels */
3081 while ((sm = SLIST_FIRST(&sg->members)) != NULL) {
3082 SLIST_REMOVE_HEAD(&sg->members, link);
3083
3084 c = sm->ch;
3085 c->sm = NULL;
3086 chn_start(c, 1);
3087 c->flags &= ~CHN_F_NOTRIGGER;
3088 CHN_UNLOCK(c);
3089
67931cc4 3090 kfree(sm, M_DEVBUF);
2a1ad637
FT
3091 }
3092
3093 SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
67931cc4 3094 kfree(sg, M_DEVBUF);
2a1ad637
FT
3095 }
3096
3097 PCM_SG_UNLOCK();
3098
3099 /*
3100 * Free_unr() may sleep, so be sure to give up the syncgroup lock
3101 * first.
3102 */
3103 if (ret == 0)
3104 free_unr(pcmsg_unrhdr, sg_id);
3105
3106 return (ret);
3107}
3108
3109/**
3110 * @brief Handler for SNDCTL_DSP_POLICY
3111 *
3112 * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment
3113 * size and count like with SNDCTL_DSP_SETFRAGMENT. Instead of the user
3114 * specifying those two parameters, s/he simply selects a number from 0..10
3115 * which corresponds to a buffer size. Smaller numbers request smaller
3116 * buffers with lower latencies (at greater overhead from more frequent
3117 * interrupts), while greater numbers behave in the opposite manner.
3118 *
3119 * The 4Front spec states that a value of 5 should be the default. However,
3120 * this implementation deviates slightly by using a linear scale without
3121 * consulting drivers. I.e., even though drivers may have different default
3122 * buffer sizes, a policy argument of 5 will have the same result across
3123 * all drivers.
3124 *
3125 * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for
3126 * more information.
3127 *
3128 * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to
3129 * work with hardware drivers directly.
3130 *
3131 * @note PCM channel arguments must not be locked by caller.
3132 *
3133 * @param wrch Pointer to opened playback channel (optional; may be NULL)
3134 * @param rdch " recording channel (optional; may be NULL)
3135 * @param policy Integer from [0:10]
3136 *
3137 * @retval 0 constant (for now)
3138 */
3139static int
3140dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
3141{
3142 int ret;
3143
3144 if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX)
3145 return (EIO);
3146
3147 /* Default: success */
3148 ret = 0;
3149
3150 if (rdch) {
3151 CHN_LOCK(rdch);
3152 ret = chn_setlatency(rdch, policy);
3153 CHN_UNLOCK(rdch);
3154 }
3155
3156 if (wrch && ret == 0) {
3157 CHN_LOCK(wrch);
3158 ret = chn_setlatency(wrch, policy);
3159 CHN_UNLOCK(wrch);
3160 }
3161
3162 if (ret)
3163 ret = EIO;
3164
3165 return (ret);
3166}
3167
3168/**
3169 * @brief Enable or disable "cooked" mode
3170 *
3171 * This is a handler for @c SNDCTL_DSP_COOKEDMODE. When in cooked mode, which
3172 * is the default, the sound system handles rate and format conversions
3173 * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only
3174 * operates with 44100Hz/16bit/signed samples).
3175 *
3176 * Disabling cooked mode is intended for applications wanting to mmap()
3177 * a sound card's buffer space directly, bypassing the FreeBSD 2-stage
3178 * feeder architecture, presumably to gain as much control over audio
3179 * hardware as possible.
3180 *
3181 * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html
3182 * for more details.
3183 *
3184 * @param wrch playback channel (optional; may be NULL)
3185 * @param rdch recording channel (optional; may be NULL)
3186 * @param enabled 0 = raw mode, 1 = cooked mode
3187 *
3188 * @retval EINVAL Operation not yet supported.
3189 */
3190static int
3191dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled)
3192{
3193
3194 /*
3195 * XXX I just don't get it. Why don't they call it
3196 * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?.
3197 * This is just plain so confusing, incoherent,
3198 * <insert any non-printable characters here>.
3199 */
3200 if (!(enabled == 1 || enabled == 0))
3201 return (EINVAL);
3202
3203 /*
3204 * I won't give in. I'm inverting its logic here and now.
3205 * Brag all you want, but "BITPERFECT" should be the better
3206 * term here.
3207 */
3208 enabled ^= 0x00000001;
3209
3210 if (wrch != NULL) {
3211 CHN_LOCK(wrch);
3212 wrch->flags &= ~CHN_F_BITPERFECT;
3213 wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000;
3214 CHN_UNLOCK(wrch);
3215 }
3216
3217 if (rdch != NULL) {
3218 CHN_LOCK(rdch);
3219 rdch->flags &= ~CHN_F_BITPERFECT;
3220 rdch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000;
3221 CHN_UNLOCK(rdch);
3222 }
3223
3224 return (0);
3225}
3226
3227/**
3228 * @brief Retrieve channel interleaving order
3229 *
3230 * This is the handler for @c SNDCTL_DSP_GET_CHNORDER.
3231 *
3232 * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html
3233 * for more details.
3234 *
3235 * @note As the ioctl definition is still under construction, FreeBSD
3236 * does not currently support SNDCTL_DSP_GET_CHNORDER.
3237 *
3238 * @param wrch playback channel (optional; may be NULL)
3239 * @param rdch recording channel (optional; may be NULL)
3240 * @param map channel map (result will be stored there)
3241 *
3242 * @retval EINVAL Operation not yet supported.
3243 */
3244static int
3245dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
3246{
3247 struct pcm_channel *ch;
3248 int ret;
3249
3250 ch = (wrch != NULL) ? wrch : rdch;
3251 if (ch != NULL) {
3252 CHN_LOCK(ch);
3253 ret = chn_oss_getorder(ch, map);
3254 CHN_UNLOCK(ch);
3255 } else
3256 ret = EINVAL;
3257
3258 return (ret);
3259}
3260
3261/**
3262 * @brief Specify channel interleaving order
3263 *
3264 * This is the handler for @c SNDCTL_DSP_SET_CHNORDER.
3265 *
3266 * @note As the ioctl definition is still under construction, FreeBSD
3267 * does not currently support @c SNDCTL_DSP_SET_CHNORDER.
3268 *
3269 * @param wrch playback channel (optional; may be NULL)
3270 * @param rdch recording channel (optional; may be NULL)
3271 * @param map channel map
3272 *
3273 * @retval EINVAL Operation not yet supported.
3274 */
3275static int
3276dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
3277{
3278 int ret;
3279
3280 ret = 0;
3281
3282 if (wrch != NULL) {
3283 CHN_LOCK(wrch);
3284 ret = chn_oss_setorder(wrch, map);
3285 CHN_UNLOCK(wrch);
3286 }
3287
3288 if (ret == 0 && rdch != NULL) {
3289 CHN_LOCK(rdch);
3290 ret = chn_oss_setorder(rdch, map);
3291 CHN_UNLOCK(rdch);
3292 }
3293
3294 return (ret);
3295}
3296
3297static int
3298dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch,
3299 int *mask)
3300{
3301 struct pcm_channel *ch;
3302 uint32_t chnmask;
3303 int ret;
3304
3305 chnmask = 0;
3306 ch = (wrch != NULL) ? wrch : rdch;
3307
3308 if (ch != NULL) {
3309 CHN_LOCK(ch);
3310 ret = chn_oss_getmask(ch, &chnmask);
3311 CHN_UNLOCK(ch);
3312 } else
3313 ret = EINVAL;
3314
3315 if (ret == 0)
3316 *mask = chnmask;
3317
3318 return (ret);
3319}
3320
3321#ifdef OSSV4_EXPERIMENT
3322/**
3323 * @brief Retrieve an audio device's label
3324 *
3325 * This is a handler for the @c SNDCTL_GETLABEL ioctl.
3326 *
3327 * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
3328 * for more details.
3329 *
3330 * From Hannu@4Front: "For example ossxmix (just like some HW mixer
3331 * consoles) can show variable "labels" for certain controls. By default
3332 * the application name (say quake) is shown as the label but
3333 * applications may change the labels themselves."
3334 *
3335 * @note As the ioctl definition is still under construction, FreeBSD
3336 * does not currently support @c SNDCTL_GETLABEL.
3337 *
3338 * @param wrch playback channel (optional; may be NULL)
3339 * @param rdch recording channel (optional; may be NULL)
3340 * @param label label gets copied here
3341 *
3342 * @retval EINVAL Operation not yet supported.
3343 */
3344static int
3345dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
3346{
3347 return (EINVAL);
3348}
3349
3350/**
3351 * @brief Specify an audio device's label
3352 *
3353 * This is a handler for the @c SNDCTL_SETLABEL ioctl. Please see the
3354 * comments for @c dsp_oss_getlabel immediately above.
3355 *
3356 * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
3357 * for more details.
3358 *
3359 * @note As the ioctl definition is still under construction, FreeBSD
3360 * does not currently support SNDCTL_SETLABEL.
3361 *
3362 * @param wrch playback channel (optional; may be NULL)
3363 * @param rdch recording channel (optional; may be NULL)
3364 * @param label label gets copied from here
3365 *
3366 * @retval EINVAL Operation not yet supported.
3367 */
3368static int
3369dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
3370{
3371 return (EINVAL);
3372}
3373
3374/**
3375 * @brief Retrieve name of currently played song
3376 *
3377 * This is a handler for the @c SNDCTL_GETSONG ioctl. Audio players could
3378 * tell the system the name of the currently playing song, which would be
3379 * visible in @c /dev/sndstat.
3380 *
3381 * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html
3382 * for more details.
3383 *
3384 * @note As the ioctl definition is still under construction, FreeBSD
3385 * does not currently support SNDCTL_GETSONG.
3386 *
3387 * @param wrch playback channel (optional; may be NULL)
3388 * @param rdch recording channel (optional; may be NULL)
3389 * @param song song name gets copied here
3390 *
3391 * @retval EINVAL Operation not yet supported.
3392 */
3393static int
3394dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
3395{
3396 return (EINVAL);
3397}
3398
3399/**
3400 * @brief Retrieve name of currently played song
3401 *
3402 * This is a handler for the @c SNDCTL_SETSONG ioctl. Audio players could
3403 * tell the system the name of the currently playing song, which would be
3404 * visible in @c /dev/sndstat.
3405 *
3406 * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html
3407 * for more details.
3408 *
3409 * @note As the ioctl definition is still under construction, FreeBSD
3410 * does not currently support SNDCTL_SETSONG.
3411 *
3412 * @param wrch playback channel (optional; may be NULL)
3413 * @param rdch recording channel (optional; may be NULL)
3414 * @param song song name gets copied from here
3415 *
3416 * @retval EINVAL Operation not yet supported.
3417 */
3418static int
3419dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
3420{
3421 return (EINVAL);
3422}
3423
3424/**
3425 * @brief Rename a device
3426 *
3427 * This is a handler for the @c SNDCTL_SETNAME ioctl.
3428 *
3429 * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for
3430 * more details.
3431 *
3432 * From Hannu@4Front: "This call is used to change the device name
3433 * reported in /dev/sndstat and ossinfo. So instead of using some generic
3434 * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull
3435 * name depending on the current context (for example 'OSS virtual wave table
3436 * synth' or 'VoIP link to London')."
3437 *
3438 * @note As the ioctl definition is still under construction, FreeBSD
3439 * does not currently support SNDCTL_SETNAME.
3440 *
3441 * @param wrch playback channel (optional; may be NULL)
3442 * @param rdch recording channel (optional; may be NULL)
3443 * @param name new device name gets copied from here
3444 *
3445 * @retval EINVAL Operation not yet supported.
3446 */
3447static int
3448dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name)
3449{
3450 return (EINVAL);
984263bc 3451}
2a1ad637 3452#endif /* !OSSV4_EXPERIMENT */