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