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