xref: /freebsd/sys/dev/sound/pcm/dsp.c (revision 0c927cdd8e6e05387fc5a9ffcb5dbe128d4ad749)
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <dev/sound/pcm/sound.h>
28 #include <sys/ctype.h>
29 
30 SND_DECLARE_FILE("$FreeBSD$");
31 
32 struct dsp_cdevinfo {
33 	struct pcm_channel *rdch, *wrch;
34 };
35 
36 #define PCM_RDCH(x)	(((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
37 #define PCM_WRCH(x)	(((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
38 
39 #define PCMDEV_ACQUIRE(x)	do {		\
40 	if ((x)->si_drv1 == NULL)		\
41 		(x)->si_drv1 = x;		\
42 } while(0)
43 
44 #define PCMDEV_RELEASE(x)	do {		\
45 	if ((x)->si_drv1 == x)			\
46 		(x)->si_drv1 = NULL;		\
47 } while(0)
48 
49 #define OLDPCM_IOCTL
50 
51 static d_open_t dsp_open;
52 static d_close_t dsp_close;
53 static d_read_t dsp_read;
54 static d_write_t dsp_write;
55 static d_ioctl_t dsp_ioctl;
56 static d_poll_t dsp_poll;
57 static d_mmap_t dsp_mmap;
58 
59 struct cdevsw dsp_cdevsw = {
60 	.d_version =	D_VERSION,
61 	.d_flags =	D_NEEDGIANT,
62 	.d_open =	dsp_open,
63 	.d_close =	dsp_close,
64 	.d_read =	dsp_read,
65 	.d_write =	dsp_write,
66 	.d_ioctl =	dsp_ioctl,
67 	.d_poll =	dsp_poll,
68 	.d_mmap =	dsp_mmap,
69 	.d_name =	"dsp",
70 };
71 
72 #ifdef USING_DEVFS
73 static eventhandler_tag dsp_ehtag = NULL;
74 static int dsp_umax = -1;
75 static int dsp_cmax = -1;
76 #endif
77 
78 static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
79 static int dsp_oss_syncstart(int sg_id);
80 static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
81 #ifdef OSSV4_EXPERIMENT
82 static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled);
83 static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
84 static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
85 static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
86 static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
87 static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
88 static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
89 static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
90 #endif
91 
92 static struct snddev_info *
93 dsp_get_info(struct cdev *dev)
94 {
95 	return (devclass_get_softc(pcm_devclass, PCMUNIT(dev)));
96 }
97 
98 static u_int32_t
99 dsp_get_flags(struct cdev *dev)
100 {
101 	device_t bdev;
102 
103 	bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
104 
105 	return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff);
106 }
107 
108 static void
109 dsp_set_flags(struct cdev *dev, u_int32_t flags)
110 {
111 	device_t bdev;
112 
113 	bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
114 
115 	if (bdev != NULL)
116 		pcm_setflags(bdev, flags);
117 }
118 
119 /*
120  * return the channels associated with an open device instance.
121  * set the priority if the device is simplex and one direction (only) is
122  * specified.
123  * lock channels specified.
124  */
125 static int
126 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
127 {
128 	struct snddev_info *d;
129 	u_int32_t flags;
130 
131 	d = dsp_get_info(dev);
132 	if (d == NULL)
133 		return -1;
134 	pcm_inprog(d, 1);
135 	pcm_lock(d);
136 	flags = dsp_get_flags(dev);
137 	KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
138 		("getchns: read and write both prioritised"));
139 
140 	if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
141 		flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
142 		dsp_set_flags(dev, flags);
143 	}
144 
145 	*rdch = PCM_RDCH(dev);
146 	*wrch = PCM_WRCH(dev);
147 	if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
148 		if (prio) {
149 			if (*rdch && flags & SD_F_PRIO_WR) {
150 				PCM_RDCH(dev) = NULL;
151 				*rdch = pcm_getfakechan(d);
152 			} else if (*wrch && flags & SD_F_PRIO_RD) {
153 				PCM_WRCH(dev) = NULL;
154 				*wrch = pcm_getfakechan(d);
155 			}
156 		}
157 
158 		pcm_getfakechan(d)->flags |= CHN_F_BUSY;
159 	}
160 	pcm_unlock(d);
161 
162 	if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
163 		CHN_LOCK(*rdch);
164 	if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
165 		CHN_LOCK(*wrch);
166 
167 	return 0;
168 }
169 
170 /* unlock specified channels */
171 static void
172 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
173 {
174 	struct snddev_info *d;
175 
176 	d = dsp_get_info(dev);
177 	if (d == NULL)
178 		return;
179 	if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
180 		CHN_UNLOCK(wrch);
181 	if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
182 		CHN_UNLOCK(rdch);
183 	pcm_inprog(d, -1);
184 }
185 
186 static void
187 dsp_cdevinfo_alloc(struct cdev *dev,
188     struct pcm_channel *rdch, struct pcm_channel *wrch)
189 {
190 	KASSERT(dev != NULL && dev->si_drv1 == dev && rdch != wrch,
191 	    ("bogus %s(), what are you trying to accomplish here?", __func__));
192 
193 	dev->si_drv1 = malloc(sizeof(struct dsp_cdevinfo), M_DEVBUF,
194 	    M_WAITOK | M_ZERO);
195 	PCM_RDCH(dev) = rdch;
196 	PCM_WRCH(dev) = wrch;
197 }
198 
199 static void
200 dsp_cdevinfo_free(struct cdev *dev)
201 {
202 	KASSERT(dev != NULL && dev->si_drv1 != NULL &&
203 	    PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL,
204 	    ("bogus %s(), what are you trying to accomplish here?", __func__));
205 
206 	free(dev->si_drv1, M_DEVBUF);
207 	dev->si_drv1 = NULL;
208 }
209 
210 /* duplex / simplex cdev type */
211 enum {
212 	DSP_CDEV_TYPE_RDONLY,		/* simplex read-only (record)   */
213 	DSP_CDEV_TYPE_WRONLY,		/* simplex write-only (play)    */
214 	DSP_CDEV_TYPE_RDWR,		/* duplex read, write, or both  */
215 };
216 
217 #define DSP_F_VALID(x)		((x) & (FREAD | FWRITE))
218 #define DSP_F_DUPLEX(x)		(((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
219 #define DSP_F_SIMPLEX(x)	(!DSP_F_DUPLEX(x))
220 #define DSP_F_READ(x)		((x) & FREAD)
221 #define DSP_F_WRITE(x)		((x) & FWRITE)
222 
223 static const struct {
224 	int type;
225 	char *name;
226 	char *sep;
227 	int use_sep;
228 	int hw;
229 	int max;
230 	uint32_t fmt, spd;
231 	int query;
232 } dsp_cdevs[] = {
233 	{ SND_DEV_DSP,         "dsp",    ".", 0, 0, 0,
234 	    AFMT_U8,       DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR   },
235 	{ SND_DEV_AUDIO,       "audio",  ".", 0, 0, 0,
236 	    AFMT_MU_LAW,   DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR   },
237 	{ SND_DEV_DSP16,       "dspW",   ".", 0, 0, 0,
238 	    AFMT_S16_LE,   DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR   },
239 	{ SND_DEV_DSPHW_PLAY,  "dsp",   ".p", 1, 1, SND_MAXHWCHAN,
240 	    AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY },
241 	{ SND_DEV_DSPHW_VPLAY, "dsp",  ".vp", 1, 1, SND_MAXVCHANS,
242 	    AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY },
243 	{ SND_DEV_DSPHW_REC,   "dsp",   ".r", 1, 1, SND_MAXHWCHAN,
244 	    AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY },
245 	{ SND_DEV_DSPHW_VREC,  "dsp",  ".vr", 1, 1, SND_MAXVCHANS,
246 	    AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY },
247 };
248 
249 static int
250 dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
251 {
252 	struct pcm_channel *rdch, *wrch;
253 	struct snddev_info *d;
254 	uint32_t fmt, spd;
255 	int i, error, devtype;
256 	int wdevunit, rdevunit;
257 
258 	/* Kind of impossible.. */
259 	if (i_dev == NULL || td == NULL)
260 		return ENODEV;
261 
262 	/* This too.. */
263 	d = dsp_get_info(i_dev);
264 	if (d == NULL)
265 		return EBADF;
266 
267 	/* Lock snddev so nobody else can monkey with it. */
268 	pcm_lock(d);
269 
270 	/*
271 	 * Try to acquire cloned device before someone else pick it.
272 	 * ENODEV means this is not a cloned droids.
273 	 */
274 	error = snd_clone_acquire(i_dev);
275 	if (!(error == 0 || error == ENODEV)) {
276 		pcm_unlock(d);
277 		return error;
278 	}
279 
280 	if (!DSP_F_VALID(flags))
281 		error = EINVAL;
282 	else if (i_dev->si_drv1 != NULL)
283 		error = EBUSY;
284 	else if (DSP_F_DUPLEX(flags) &&
285 	    (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
286 		error = ENOTSUP;
287 	else
288 		error = 0;
289 
290 	if (error != 0) {
291 		(void)snd_clone_release(i_dev);
292 		pcm_unlock(d);
293 		return error;
294 	}
295 
296 	/*
297 	 * Fake busy state by pointing si_drv1 to something else since
298 	 * we have to give up locking somewhere during setup process.
299 	 */
300 	PCMDEV_ACQUIRE(i_dev);
301 
302 	devtype = PCMDEV(i_dev);
303 	wdevunit = -1;
304 	rdevunit = -1;
305 	fmt = 0;
306 	spd = 0;
307 
308 	for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
309 		if (devtype != dsp_cdevs[i].type)
310 			continue;
311 		if (DSP_F_SIMPLEX(flags) &&
312 		    ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY &&
313 		    DSP_F_READ(flags)) ||
314 		    (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY &&
315 		    DSP_F_WRITE(flags)))) {
316 			/*
317 			 * simplex, opposite direction? Please be gone..
318 			 */
319 			(void)snd_clone_release(i_dev);
320 			PCMDEV_RELEASE(i_dev);
321 			pcm_unlock(d);
322 			return ENOTSUP;
323 		}
324 		if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY)
325 			wdevunit = dev2unit(i_dev);
326 		else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY)
327 			rdevunit = dev2unit(i_dev);
328 		fmt = dsp_cdevs[i].fmt;
329 		spd = dsp_cdevs[i].spd;
330 		break;
331 	}
332 
333 	/* No matching devtype? */
334 	if (fmt == 0 || spd == 0)
335 		panic("impossible devtype %d", devtype);
336 
337 	rdch = NULL;
338 	wrch = NULL;
339 
340 	/*
341 	 * if we get here, the open request is valid- either:
342 	 *   * we were previously not open
343 	 *   * we were open for play xor record and the opener wants
344 	 *     the non-open direction
345 	 */
346 	if (DSP_F_READ(flags)) {
347 		/* open for read */
348 		pcm_unlock(d);
349 		error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid,
350 		    rdevunit);
351 
352 		if (error == 0 && (chn_reset(rdch, fmt) != 0 ||
353 		    (chn_setspeed(rdch, spd) != 0)))
354 			error = ENODEV;
355 
356 		if (error != 0) {
357 			if (rdch != NULL)
358 				pcm_chnrelease(rdch);
359 			pcm_lock(d);
360 			(void)snd_clone_release(i_dev);
361 			PCMDEV_RELEASE(i_dev);
362 			pcm_unlock(d);
363 			return error;
364 		}
365 
366 		if (flags & O_NONBLOCK)
367 			rdch->flags |= CHN_F_NBIO;
368 		pcm_chnref(rdch, 1);
369 	 	CHN_UNLOCK(rdch);
370 		pcm_lock(d);
371 	}
372 
373 	if (DSP_F_WRITE(flags)) {
374 		/* open for write */
375 		pcm_unlock(d);
376 		error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid,
377 		    wdevunit);
378 
379 		if (error == 0 && (chn_reset(wrch, fmt) != 0 ||
380 		    (chn_setspeed(wrch, spd) != 0)))
381 			error = ENODEV;
382 
383 		if (error != 0) {
384 			if (wrch != NULL)
385 				pcm_chnrelease(wrch);
386 			if (rdch != NULL) {
387 				/*
388 				 * Lock, deref and release previously
389 				 * created record channel
390 				 */
391 				CHN_LOCK(rdch);
392 				pcm_chnref(rdch, -1);
393 				pcm_chnrelease(rdch);
394 			}
395 			pcm_lock(d);
396 			(void)snd_clone_release(i_dev);
397 			PCMDEV_RELEASE(i_dev);
398 			pcm_unlock(d);
399 			return error;
400 		}
401 
402 		if (flags & O_NONBLOCK)
403 			wrch->flags |= CHN_F_NBIO;
404 		pcm_chnref(wrch, 1);
405 		CHN_UNLOCK(wrch);
406 		pcm_lock(d);
407 	}
408 
409 	/*
410 	 * Increase clone refcount for its automatic garbage collector.
411 	 */
412 	(void)snd_clone_ref(i_dev);
413 
414 	pcm_unlock(d);
415 
416 	/*
417 	 * We're done. Allocate and point si_drv1 to a real
418 	 * allocated structure.
419 	 */
420 	dsp_cdevinfo_alloc(i_dev, rdch, wrch);
421 
422 	return 0;
423 }
424 
425 static int
426 dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
427 {
428 	struct pcm_channel *rdch, *wrch;
429 	struct snddev_info *d;
430 	int refs, sg_ids[2];
431 
432 	d = dsp_get_info(i_dev);
433 	if (d == NULL)
434 		return EBADF;
435 	pcm_lock(d);
436 	rdch = PCM_RDCH(i_dev);
437 	wrch = PCM_WRCH(i_dev);
438 
439 	/*
440 	 * Free_unr() may sleep, so store released syncgroup IDs until after
441 	 * all locks are released.
442 	 */
443 	sg_ids[0] = sg_ids[1] = 0;
444 
445 	if (rdch || wrch) {
446 		refs = 0;
447 		pcm_unlock(d);
448 		if (rdch) {
449 			/*
450 			 * The channel itself need not be locked because:
451 			 *   a)  Adding a channel to a syncgroup happens only in dsp_ioctl(),
452 			 *       which cannot run concurrently to dsp_close().
453 			 *   b)  The syncmember pointer (sm) is protected by the global
454 			 *       syncgroup list lock.
455 			 *   c)  A channel can't just disappear, invalidating pointers,
456 			 *       unless it's closed/dereferenced first.
457 			 */
458 			PCM_SG_LOCK();
459 			sg_ids[0] = chn_syncdestroy(rdch);
460 			PCM_SG_UNLOCK();
461 
462 			CHN_LOCK(rdch);
463 			refs += pcm_chnref(rdch, -1);
464 			chn_abort(rdch); /* won't sleep */
465 			rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
466 			chn_reset(rdch, 0);
467 			pcm_chnrelease(rdch);
468 		}
469 		if (wrch) {
470 			/*
471 			 * Please see block above.
472 			 */
473 			PCM_SG_LOCK();
474 			sg_ids[1] = chn_syncdestroy(wrch);
475 			PCM_SG_UNLOCK();
476 
477 			CHN_LOCK(wrch);
478 			refs += pcm_chnref(wrch, -1);
479 			chn_flush(wrch); /* may sleep */
480 			wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
481 			chn_reset(wrch, 0);
482 			pcm_chnrelease(wrch);
483 		}
484 
485 		pcm_lock(d);
486 		if (rdch)
487 			PCM_RDCH(i_dev) = NULL;
488 		if (wrch)
489 			PCM_WRCH(i_dev) = NULL;
490 		/*
491 		 * If there are no more references, release the channels.
492 		 */
493 		if (refs == 0 && PCM_RDCH(i_dev) == NULL &&
494 		    PCM_WRCH(i_dev) == NULL) {
495 			if (pcm_getfakechan(d))
496 				pcm_getfakechan(d)->flags = 0;
497 			/* What is this?!? */
498 			dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
499 			dsp_cdevinfo_free(i_dev);
500 			/*
501 			 * Release clone busy state and unref it
502 			 * so the automatic garbage collector will
503 			 * get the hint and do the remaining cleanup
504 			 * process.
505 			 */
506 			(void)snd_clone_release(i_dev);
507 			(void)snd_clone_unref(i_dev);
508 		}
509 	}
510 
511 	pcm_unlock(d);
512 
513 	if (sg_ids[0])
514 		free_unr(pcmsg_unrhdr, sg_ids[0]);
515 	if (sg_ids[1])
516 		free_unr(pcmsg_unrhdr, sg_ids[1]);
517 
518 	return 0;
519 }
520 
521 static int
522 dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
523 {
524 	struct pcm_channel *rdch, *wrch;
525 	int ret;
526 
527 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
528 
529 	if (rdch == NULL || !(rdch->flags & CHN_F_BUSY))
530 		return EBADF;
531 
532 	if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
533 		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
534 		return EINVAL;
535 	}
536 	if (!(rdch->flags & CHN_F_RUNNING))
537 		rdch->flags |= CHN_F_RUNNING;
538 	ret = chn_read(rdch, buf);
539 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
540 
541 	return ret;
542 }
543 
544 static int
545 dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
546 {
547 	struct pcm_channel *rdch, *wrch;
548 	int ret;
549 
550 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
551 
552 	if (wrch == NULL || !(wrch->flags & CHN_F_BUSY))
553 		return EBADF;
554 
555 	if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
556 		relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
557 		return EINVAL;
558 	}
559 	if (!(wrch->flags & CHN_F_RUNNING))
560 		wrch->flags |= CHN_F_RUNNING;
561 
562 	/*
563 	 * Chn_write() must give up channel lock in order to copy bytes from
564 	 * userland, so up the "in progress" counter to make sure someone
565 	 * else doesn't come along and muss up the buffer.
566 	 */
567 	++wrch->inprog;
568 	ret = chn_write(wrch, buf);
569 	--wrch->inprog;
570 	cv_signal(&wrch->cv);
571 
572 	relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
573 
574 	return ret;
575 }
576 
577 static int
578 dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
579 {
580     	struct pcm_channel *chn, *rdch, *wrch;
581 	struct snddev_info *d;
582 	int kill;
583     	int ret = 0, *arg_i = (int *)arg, tmp;
584 	int xcmd;
585 
586 	xcmd = 0;
587 
588 	/*
589 	 * this is an evil hack to allow broken apps to perform mixer ioctls
590 	 * on dsp devices.
591 	 */
592 
593 	d = dsp_get_info(i_dev);
594 	if (d == NULL)
595 		return EBADF;
596 	if (IOCGROUP(cmd) == 'M') {
597 		/*
598 		 * This is at least, a bug to bug compatible with OSS.
599 		 */
600 		if (d->mixer_dev != NULL)
601 			return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
602 		else
603 			return EBADF;
604 	}
605 
606 	/*
607 	 * Certain ioctls may be made on any type of device (audio, mixer,
608 	 * and MIDI).  Handle those special cases here.
609 	 */
610 	if (IOCGROUP(cmd) == 'X') {
611 		switch(cmd) {
612 		case SNDCTL_SYSINFO:
613 			sound_oss_sysinfo((oss_sysinfo *)arg);
614 			break;
615 		case SNDCTL_AUDIOINFO:
616 			ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
617 			break;
618 		case SNDCTL_MIXERINFO:
619 			ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
620 			break;
621 		default:
622 			ret = EINVAL;
623 		}
624 
625 		return ret;
626 	}
627 
628 	getchns(i_dev, &rdch, &wrch, 0);
629 
630 	kill = 0;
631 	if (wrch && (wrch->flags & CHN_F_DEAD))
632 		kill |= 1;
633 	if (rdch && (rdch->flags & CHN_F_DEAD))
634 		kill |= 2;
635 	if (kill == 3) {
636 		relchns(i_dev, rdch, wrch, 0);
637 		return EINVAL;
638 	}
639 	if (kill & 1)
640 		wrch = NULL;
641 	if (kill & 2)
642 		rdch = NULL;
643 
644     	switch(cmd) {
645 #ifdef OLDPCM_IOCTL
646     	/*
647      	 * we start with the new ioctl interface.
648      	 */
649     	case AIONWRITE:	/* how many bytes can write ? */
650 		if (wrch) {
651 			CHN_LOCK(wrch);
652 /*
653 		if (wrch && wrch->bufhard.dl)
654 			while (chn_wrfeed(wrch) == 0);
655 */
656 			*arg_i = sndbuf_getfree(wrch->bufsoft);
657 			CHN_UNLOCK(wrch);
658 		} else {
659 			*arg_i = 0;
660 			ret = EINVAL;
661 		}
662 		break;
663 
664     	case AIOSSIZE:     /* set the current blocksize */
665 		{
666 	    		struct snd_size *p = (struct snd_size *)arg;
667 
668 			p->play_size = 0;
669 			p->rec_size = 0;
670 	    		if (wrch) {
671 				CHN_LOCK(wrch);
672 				chn_setblocksize(wrch, 2, p->play_size);
673 				p->play_size = sndbuf_getblksz(wrch->bufsoft);
674 				CHN_UNLOCK(wrch);
675 			}
676 	    		if (rdch) {
677 				CHN_LOCK(rdch);
678 				chn_setblocksize(rdch, 2, p->rec_size);
679 				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
680 				CHN_UNLOCK(rdch);
681 			}
682 		}
683 		break;
684     	case AIOGSIZE:	/* get the current blocksize */
685 		{
686 	    		struct snd_size *p = (struct snd_size *)arg;
687 
688 	    		if (wrch) {
689 				CHN_LOCK(wrch);
690 				p->play_size = sndbuf_getblksz(wrch->bufsoft);
691 				CHN_UNLOCK(wrch);
692 			}
693 	    		if (rdch) {
694 				CHN_LOCK(rdch);
695 				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
696 				CHN_UNLOCK(rdch);
697 			}
698 		}
699 		break;
700 
701     	case AIOSFMT:
702     	case AIOGFMT:
703 		{
704 	    		snd_chan_param *p = (snd_chan_param *)arg;
705 
706 			if (cmd == AIOSFMT &&
707 			    ((p->play_format != 0 && p->play_rate == 0) ||
708 			    (p->rec_format != 0 && p->rec_rate == 0))) {
709 				ret = EINVAL;
710 				break;
711 			}
712 	    		if (wrch) {
713 				CHN_LOCK(wrch);
714 				if (cmd == AIOSFMT && p->play_format != 0) {
715 					chn_setformat(wrch, p->play_format);
716 					chn_setspeed(wrch, p->play_rate);
717 				}
718 	    			p->play_rate = wrch->speed;
719 	    			p->play_format = wrch->format;
720 				CHN_UNLOCK(wrch);
721 			} else {
722 	    			p->play_rate = 0;
723 	    			p->play_format = 0;
724 	    		}
725 	    		if (rdch) {
726 				CHN_LOCK(rdch);
727 				if (cmd == AIOSFMT && p->rec_format != 0) {
728 					chn_setformat(rdch, p->rec_format);
729 					chn_setspeed(rdch, p->rec_rate);
730 				}
731 				p->rec_rate = rdch->speed;
732 				p->rec_format = rdch->format;
733 				CHN_UNLOCK(rdch);
734 			} else {
735 	    			p->rec_rate = 0;
736 	    			p->rec_format = 0;
737 	    		}
738 		}
739 		break;
740 
741     	case AIOGCAP:     /* get capabilities */
742 		{
743 	    		snd_capabilities *p = (snd_capabilities *)arg;
744 			struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
745 			struct cdev *pdev;
746 
747 			if (rdch) {
748 				CHN_LOCK(rdch);
749 				rcaps = chn_getcaps(rdch);
750 			}
751 			if (wrch) {
752 				CHN_LOCK(wrch);
753 				pcaps = chn_getcaps(wrch);
754 			}
755 	    		p->rate_min = max(rcaps? rcaps->minspeed : 0,
756 	                      		  pcaps? pcaps->minspeed : 0);
757 	    		p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
758 	                      		  pcaps? pcaps->maxspeed : 1000000);
759 	    		p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
760 	                     		 wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
761 			/* XXX bad on sb16 */
762 	    		p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
763 			 	     (wrch? chn_getformats(wrch) : 0xffffffff);
764 			if (rdch && wrch)
765 				p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
766 			pdev = d->mixer_dev;
767 	    		p->mixers = 1; /* default: one mixer */
768 	    		p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
769 	    		p->left = p->right = 100;
770 			if (rdch)
771 				CHN_UNLOCK(rdch);
772 			if (wrch)
773 				CHN_UNLOCK(wrch);
774 		}
775 		break;
776 
777     	case AIOSTOP:
778 		if (*arg_i == AIOSYNC_PLAY && wrch) {
779 			CHN_LOCK(wrch);
780 			*arg_i = chn_abort(wrch);
781 			CHN_UNLOCK(wrch);
782 		} else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
783 			CHN_LOCK(rdch);
784 			*arg_i = chn_abort(rdch);
785 			CHN_UNLOCK(rdch);
786 		} else {
787 	   	 	printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
788 	    		*arg_i = 0;
789 		}
790 		break;
791 
792     	case AIOSYNC:
793 		printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
794 	    		((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
795 		break;
796 #endif
797 	/*
798 	 * here follow the standard ioctls (filio.h etc.)
799 	 */
800     	case FIONREAD: /* get # bytes to read */
801 		if (rdch) {
802 			CHN_LOCK(rdch);
803 /*			if (rdch && rdch->bufhard.dl)
804 				while (chn_rdfeed(rdch) == 0);
805 */
806 			*arg_i = sndbuf_getready(rdch->bufsoft);
807 			CHN_UNLOCK(rdch);
808 		} else {
809 			*arg_i = 0;
810 			ret = EINVAL;
811 		}
812 		break;
813 
814     	case FIOASYNC: /*set/clear async i/o */
815 		DEB( printf("FIOASYNC\n") ; )
816 		break;
817 
818     	case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */
819     	case FIONBIO: /* set/clear non-blocking i/o */
820 		if (rdch) {
821 			CHN_LOCK(rdch);
822 			if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
823 				rdch->flags |= CHN_F_NBIO;
824 			else
825 				rdch->flags &= ~CHN_F_NBIO;
826 			CHN_UNLOCK(rdch);
827 		}
828 		if (wrch) {
829 			CHN_LOCK(wrch);
830 			if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i)
831 				wrch->flags |= CHN_F_NBIO;
832 			else
833 				wrch->flags &= ~CHN_F_NBIO;
834 			CHN_UNLOCK(wrch);
835 		}
836 		break;
837 
838     	/*
839 	 * Finally, here is the linux-compatible ioctl interface
840 	 */
841 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
842     	case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
843     	case SNDCTL_DSP_GETBLKSIZE:
844 		chn = wrch ? wrch : rdch;
845 		if (chn) {
846 			CHN_LOCK(chn);
847 			*arg_i = sndbuf_getblksz(chn->bufsoft);
848 			CHN_UNLOCK(chn);
849 		} else {
850 			*arg_i = 0;
851 			ret = EINVAL;
852 		}
853 		break ;
854 
855     	case SNDCTL_DSP_SETBLKSIZE:
856 		RANGE(*arg_i, 16, 65536);
857 		if (wrch) {
858 			CHN_LOCK(wrch);
859 			chn_setblocksize(wrch, 2, *arg_i);
860 			CHN_UNLOCK(wrch);
861 		}
862 		if (rdch) {
863 			CHN_LOCK(rdch);
864 			chn_setblocksize(rdch, 2, *arg_i);
865 			CHN_UNLOCK(rdch);
866 		}
867 		break;
868 
869     	case SNDCTL_DSP_RESET:
870 		DEB(printf("dsp reset\n"));
871 		if (wrch) {
872 			CHN_LOCK(wrch);
873 			chn_abort(wrch);
874 			chn_resetbuf(wrch);
875 			CHN_UNLOCK(wrch);
876 		}
877 		if (rdch) {
878 			CHN_LOCK(rdch);
879 			chn_abort(rdch);
880 			chn_resetbuf(rdch);
881 			CHN_UNLOCK(rdch);
882 		}
883 		break;
884 
885     	case SNDCTL_DSP_SYNC:
886 		DEB(printf("dsp sync\n"));
887 		/* chn_sync may sleep */
888 		if (wrch) {
889 			CHN_LOCK(wrch);
890 			chn_sync(wrch, 0);
891 			CHN_UNLOCK(wrch);
892 		}
893 		break;
894 
895     	case SNDCTL_DSP_SPEED:
896 		/* chn_setspeed may sleep */
897 		tmp = 0;
898 		if (wrch) {
899 			CHN_LOCK(wrch);
900 			ret = chn_setspeed(wrch, *arg_i);
901 			tmp = wrch->speed;
902 			CHN_UNLOCK(wrch);
903 		}
904 		if (rdch && ret == 0) {
905 			CHN_LOCK(rdch);
906 			ret = chn_setspeed(rdch, *arg_i);
907 			if (tmp == 0)
908 				tmp = rdch->speed;
909 			CHN_UNLOCK(rdch);
910 		}
911 		*arg_i = tmp;
912 		break;
913 
914     	case SOUND_PCM_READ_RATE:
915 		chn = wrch ? wrch : rdch;
916 		if (chn) {
917 			CHN_LOCK(chn);
918 			*arg_i = chn->speed;
919 			CHN_UNLOCK(chn);
920 		} else {
921 			*arg_i = 0;
922 			ret = EINVAL;
923 		}
924 		break;
925 
926     	case SNDCTL_DSP_STEREO:
927 		tmp = -1;
928 		*arg_i = (*arg_i)? AFMT_STEREO : 0;
929 		if (wrch) {
930 			CHN_LOCK(wrch);
931 			ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
932 			tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
933 			CHN_UNLOCK(wrch);
934 		}
935 		if (rdch && ret == 0) {
936 			CHN_LOCK(rdch);
937 			ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
938 			if (tmp == -1)
939 				tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
940 			CHN_UNLOCK(rdch);
941 		}
942 		*arg_i = tmp;
943 		break;
944 
945     	case SOUND_PCM_WRITE_CHANNELS:
946 /*	case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
947 		if (*arg_i != 0) {
948 			tmp = 0;
949 			*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
950 	  		if (wrch) {
951 				CHN_LOCK(wrch);
952 				ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
953 				tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
954 				CHN_UNLOCK(wrch);
955 			}
956 			if (rdch && ret == 0) {
957 				CHN_LOCK(rdch);
958 				ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
959 				if (tmp == 0)
960 					tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
961 				CHN_UNLOCK(rdch);
962 			}
963 			*arg_i = tmp;
964 		} else {
965 			chn = wrch ? wrch : rdch;
966 			CHN_LOCK(chn);
967 			*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
968 			CHN_UNLOCK(chn);
969 		}
970 		break;
971 
972     	case SOUND_PCM_READ_CHANNELS:
973 		chn = wrch ? wrch : rdch;
974 		if (chn) {
975 			CHN_LOCK(chn);
976 			*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
977 			CHN_UNLOCK(chn);
978 		} else {
979 			*arg_i = 0;
980 			ret = EINVAL;
981 		}
982 		break;
983 
984     	case SNDCTL_DSP_GETFMTS:	/* returns a mask of supported fmts */
985 		chn = wrch ? wrch : rdch;
986 		if (chn) {
987 			CHN_LOCK(chn);
988 			*arg_i = chn_getformats(chn);
989 			CHN_UNLOCK(chn);
990 		} else {
991 			*arg_i = 0;
992 			ret = EINVAL;
993 		}
994 		break ;
995 
996     	case SNDCTL_DSP_SETFMT:	/* sets _one_ format */
997 		if ((*arg_i != AFMT_QUERY)) {
998 			tmp = 0;
999 			if (wrch) {
1000 				CHN_LOCK(wrch);
1001 				ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
1002 				tmp = wrch->format & ~AFMT_STEREO;
1003 				CHN_UNLOCK(wrch);
1004 			}
1005 			if (rdch && ret == 0) {
1006 				CHN_LOCK(rdch);
1007 				ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
1008 				if (tmp == 0)
1009 					tmp = rdch->format & ~AFMT_STEREO;
1010 				CHN_UNLOCK(rdch);
1011 			}
1012 			*arg_i = tmp;
1013 		} else {
1014 			chn = wrch ? wrch : rdch;
1015 			CHN_LOCK(chn);
1016 			*arg_i = chn->format & ~AFMT_STEREO;
1017 			CHN_UNLOCK(chn);
1018 		}
1019 		break;
1020 
1021     	case SNDCTL_DSP_SETFRAGMENT:
1022 		DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
1023 		{
1024 			u_int32_t fragln = (*arg_i) & 0x0000ffff;
1025 			u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
1026 			u_int32_t fragsz;
1027 			u_int32_t r_maxfrags, r_fragsz;
1028 
1029 			RANGE(fragln, 4, 16);
1030 			fragsz = 1 << fragln;
1031 
1032 			if (maxfrags == 0)
1033 				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
1034 			if (maxfrags < 2)
1035 				maxfrags = 2;
1036 			if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
1037 				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
1038 
1039 			DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
1040 		    	if (rdch) {
1041 				CHN_LOCK(rdch);
1042 				ret = chn_setblocksize(rdch, maxfrags, fragsz);
1043 				r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
1044 				r_fragsz = sndbuf_getblksz(rdch->bufsoft);
1045 				CHN_UNLOCK(rdch);
1046 			} else {
1047 				r_maxfrags = maxfrags;
1048 				r_fragsz = fragsz;
1049 			}
1050 		    	if (wrch && ret == 0) {
1051 				CHN_LOCK(wrch);
1052 				ret = chn_setblocksize(wrch, maxfrags, fragsz);
1053  				maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
1054 				fragsz = sndbuf_getblksz(wrch->bufsoft);
1055 				CHN_UNLOCK(wrch);
1056 			} else { /* use whatever came from the read channel */
1057 				maxfrags = r_maxfrags;
1058 				fragsz = r_fragsz;
1059 			}
1060 
1061 			fragln = 0;
1062 			while (fragsz > 1) {
1063 				fragln++;
1064 				fragsz >>= 1;
1065 			}
1066 	    		*arg_i = (maxfrags << 16) | fragln;
1067 		}
1068 		break;
1069 
1070     	case SNDCTL_DSP_GETISPACE:
1071 		/* return the size of data available in the input queue */
1072 		{
1073 	    		audio_buf_info *a = (audio_buf_info *)arg;
1074 	    		if (rdch) {
1075 	        		struct snd_dbuf *bs = rdch->bufsoft;
1076 
1077 				CHN_LOCK(rdch);
1078 				a->bytes = sndbuf_getready(bs);
1079 	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
1080 	        		a->fragstotal = sndbuf_getblkcnt(bs);
1081 	        		a->fragsize = sndbuf_getblksz(bs);
1082 				CHN_UNLOCK(rdch);
1083 	    		}
1084 		}
1085 		break;
1086 
1087     	case SNDCTL_DSP_GETOSPACE:
1088 		/* return space available in the output queue */
1089 		{
1090 	    		audio_buf_info *a = (audio_buf_info *)arg;
1091 	    		if (wrch) {
1092 	        		struct snd_dbuf *bs = wrch->bufsoft;
1093 
1094 				CHN_LOCK(wrch);
1095 				/* XXX abusive DMA update: chn_wrupdate(wrch); */
1096 				a->bytes = sndbuf_getfree(bs);
1097 	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
1098 	        		a->fragstotal = sndbuf_getblkcnt(bs);
1099 	        		a->fragsize = sndbuf_getblksz(bs);
1100 				CHN_UNLOCK(wrch);
1101 	    		}
1102 		}
1103 		break;
1104 
1105     	case SNDCTL_DSP_GETIPTR:
1106 		{
1107 	    		count_info *a = (count_info *)arg;
1108 	    		if (rdch) {
1109 	        		struct snd_dbuf *bs = rdch->bufsoft;
1110 
1111 				CHN_LOCK(rdch);
1112 				/* XXX abusive DMA update: chn_rdupdate(rdch); */
1113 	        		a->bytes = sndbuf_gettotal(bs);
1114 	        		a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
1115 	        		a->ptr = sndbuf_getreadyptr(bs);
1116 				rdch->blocks = sndbuf_getblocks(bs);
1117 				CHN_UNLOCK(rdch);
1118 	    		} else
1119 				ret = EINVAL;
1120 		}
1121 		break;
1122 
1123     	case SNDCTL_DSP_GETOPTR:
1124 		{
1125 	    		count_info *a = (count_info *)arg;
1126 	    		if (wrch) {
1127 	        		struct snd_dbuf *bs = wrch->bufsoft;
1128 
1129 				CHN_LOCK(wrch);
1130 				/* XXX abusive DMA update: chn_wrupdate(wrch); */
1131 	        		a->bytes = sndbuf_gettotal(bs);
1132 	        		a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
1133 	        		a->ptr = sndbuf_getreadyptr(bs);
1134 				wrch->blocks = sndbuf_getblocks(bs);
1135 				CHN_UNLOCK(wrch);
1136 	    		} else
1137 				ret = EINVAL;
1138 		}
1139 		break;
1140 
1141     	case SNDCTL_DSP_GETCAPS:
1142 		*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
1143 		if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1144 			*arg_i |= DSP_CAP_DUPLEX;
1145 		break;
1146 
1147     	case SOUND_PCM_READ_BITS:
1148 		chn = wrch ? wrch : rdch;
1149 		if (chn) {
1150 			CHN_LOCK(chn);
1151 			if (chn->format & AFMT_8BIT)
1152 				*arg_i = 8;
1153 			else if (chn->format & AFMT_16BIT)
1154 				*arg_i = 16;
1155 			else if (chn->format & AFMT_24BIT)
1156 				*arg_i = 24;
1157 			else if (chn->format & AFMT_32BIT)
1158 				*arg_i = 32;
1159 			else
1160 				ret = EINVAL;
1161 			CHN_UNLOCK(chn);
1162 		} else {
1163 			*arg_i = 0;
1164 			ret = EINVAL;
1165 		}
1166 		break;
1167 
1168     	case SNDCTL_DSP_SETTRIGGER:
1169 		if (rdch) {
1170 			CHN_LOCK(rdch);
1171 			rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1172 		    	if (*arg_i & PCM_ENABLE_INPUT)
1173 				chn_start(rdch, 1);
1174 			else
1175 				rdch->flags |= CHN_F_NOTRIGGER;
1176 			CHN_UNLOCK(rdch);
1177 		}
1178 		if (wrch) {
1179 			CHN_LOCK(wrch);
1180 			wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1181 		    	if (*arg_i & PCM_ENABLE_OUTPUT)
1182 				chn_start(wrch, 1);
1183 			else
1184 				wrch->flags |= CHN_F_NOTRIGGER;
1185 			CHN_UNLOCK(wrch);
1186 		}
1187 		break;
1188 
1189     	case SNDCTL_DSP_GETTRIGGER:
1190 		*arg_i = 0;
1191 		if (wrch) {
1192 			CHN_LOCK(wrch);
1193 			if (wrch->flags & CHN_F_TRIGGERED)
1194 				*arg_i |= PCM_ENABLE_OUTPUT;
1195 			CHN_UNLOCK(wrch);
1196 		}
1197 		if (rdch) {
1198 			CHN_LOCK(rdch);
1199 			if (rdch->flags & CHN_F_TRIGGERED)
1200 				*arg_i |= PCM_ENABLE_INPUT;
1201 			CHN_UNLOCK(rdch);
1202 		}
1203 		break;
1204 
1205 	case SNDCTL_DSP_GETODELAY:
1206 		if (wrch) {
1207 	        	struct snd_dbuf *bs = wrch->bufsoft;
1208 
1209 			CHN_LOCK(wrch);
1210 			/* XXX abusive DMA update: chn_wrupdate(wrch); */
1211 			*arg_i = sndbuf_getready(bs);
1212 			CHN_UNLOCK(wrch);
1213 		} else
1214 			ret = EINVAL;
1215 		break;
1216 
1217     	case SNDCTL_DSP_POST:
1218 		if (wrch) {
1219 			CHN_LOCK(wrch);
1220 			wrch->flags &= ~CHN_F_NOTRIGGER;
1221 			chn_start(wrch, 1);
1222 			CHN_UNLOCK(wrch);
1223 		}
1224 		break;
1225 
1226 	case SNDCTL_DSP_SETDUPLEX:
1227 		/*
1228 		 * switch to full-duplex mode if card is in half-duplex
1229 		 * mode and is able to work in full-duplex mode
1230 		 */
1231 		if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1232 			dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
1233 		break;
1234 
1235 	/*
1236 	 * The following four ioctls are simple wrappers around mixer_ioctl
1237 	 * with no further processing.  xcmd is short for "translated
1238 	 * command".
1239 	 */
1240 	case SNDCTL_DSP_GETRECVOL:
1241 		if (xcmd == 0)
1242 			xcmd = SOUND_MIXER_READ_RECLEV;
1243 		/* FALLTHROUGH */
1244 	case SNDCTL_DSP_SETRECVOL:
1245 		if (xcmd == 0)
1246 			xcmd = SOUND_MIXER_WRITE_RECLEV;
1247 		/* FALLTHROUGH */
1248 	case SNDCTL_DSP_GETPLAYVOL:
1249 		if (xcmd == 0)
1250 			xcmd = SOUND_MIXER_READ_PCM;
1251 		/* FALLTHROUGH */
1252 	case SNDCTL_DSP_SETPLAYVOL:
1253 		if (xcmd == 0)
1254 			xcmd = SOUND_MIXER_WRITE_PCM;
1255 
1256 		if (d->mixer_dev != NULL)
1257 			ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td);
1258 		else
1259 			ret = ENOTSUP;
1260 		break;
1261 
1262 	case SNDCTL_DSP_GET_RECSRC_NAMES:
1263 	case SNDCTL_DSP_GET_RECSRC:
1264 	case SNDCTL_DSP_SET_RECSRC:
1265 		if (d->mixer_dev != NULL)
1266 			ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
1267 		else
1268 			ret = ENOTSUP;
1269 		break;
1270 
1271 	/*
1272 	 * The following 3 ioctls aren't very useful at the moment.  For
1273 	 * now, only a single channel is associated with a cdev (/dev/dspN
1274 	 * instance), so there's only a single output routing to use (i.e.,
1275 	 * the wrch bound to this cdev).
1276 	 */
1277 	case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1278 		{
1279 			oss_mixer_enuminfo *ei;
1280 			ei = (oss_mixer_enuminfo *)arg;
1281 			ei->dev = 0;
1282 			ei->ctrl = 0;
1283 			ei->version = 0; /* static for now */
1284 			ei->strindex[0] = 0;
1285 
1286 			if (wrch != NULL) {
1287 				ei->nvalues = 1;
1288 				strlcpy(ei->strings, wrch->name,
1289 					sizeof(ei->strings));
1290 			} else {
1291 				ei->nvalues = 0;
1292 				ei->strings[0] = '\0';
1293 			}
1294 		}
1295 		break;
1296 	case SNDCTL_DSP_GET_PLAYTGT:
1297 	case SNDCTL_DSP_SET_PLAYTGT:	/* yes, they are the same for now */
1298 		/*
1299 		 * Re: SET_PLAYTGT
1300 		 *   OSSv4: "The value that was accepted by the device will
1301 		 *   be returned back in the variable pointed by the
1302 		 *   argument."
1303 		 */
1304 		if (wrch != NULL)
1305 			*arg_i = 0;
1306 		else
1307 			ret = EINVAL;
1308 		break;
1309 
1310 	case SNDCTL_DSP_SILENCE:
1311 	/*
1312 	 * Flush the software (pre-feed) buffer, but try to minimize playback
1313 	 * interruption.  (I.e., record unplayed samples with intent to
1314 	 * restore by SNDCTL_DSP_SKIP.) Intended for application "pause"
1315 	 * functionality.
1316 	 */
1317 		if (wrch == NULL)
1318 			ret = EINVAL;
1319 		else {
1320 			struct snd_dbuf *bs;
1321 			CHN_LOCK(wrch);
1322 			while (wrch->inprog != 0)
1323 				cv_wait(&wrch->cv, wrch->lock);
1324 			bs = wrch->bufsoft;
1325 			if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) {
1326 				bs->sl = sndbuf_getready(bs);
1327 				sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs));
1328 				sndbuf_fillsilence(bs);
1329 				chn_start(wrch, 0);
1330 			}
1331 			CHN_UNLOCK(wrch);
1332 		}
1333 		break;
1334 
1335 	case SNDCTL_DSP_SKIP:
1336 	/*
1337 	 * OSSv4 docs: "This ioctl call discards all unplayed samples in the
1338 	 * playback buffer by moving the current write position immediately
1339 	 * before the point where the device is currently reading the samples."
1340 	 */
1341 		if (wrch == NULL)
1342 			ret = EINVAL;
1343 		else {
1344 			struct snd_dbuf *bs;
1345 			CHN_LOCK(wrch);
1346 			while (wrch->inprog != 0)
1347 				cv_wait(&wrch->cv, wrch->lock);
1348 			bs = wrch->bufsoft;
1349 			if ((bs->shadbuf != NULL) && (bs->sl > 0)) {
1350 				sndbuf_softreset(bs);
1351 				sndbuf_acquire(bs, bs->shadbuf, bs->sl);
1352 				bs->sl = 0;
1353 				chn_start(wrch, 0);
1354 			}
1355 			CHN_UNLOCK(wrch);
1356 		}
1357 		break;
1358 
1359 	case SNDCTL_DSP_CURRENT_OPTR:
1360 	case SNDCTL_DSP_CURRENT_IPTR:
1361 	/**
1362 	 * @note Changing formats resets the buffer counters, which differs
1363 	 * 	 from the 4Front drivers.  However, I don't expect this to be
1364 	 * 	 much of a problem.
1365 	 *
1366 	 * @note In a test where @c CURRENT_OPTR is called immediately after write
1367 	 * 	 returns, this driver is about 32K samples behind whereas
1368 	 * 	 4Front's is about 8K samples behind.  Should determine source
1369 	 * 	 of discrepancy, even if only out of curiosity.
1370 	 *
1371 	 * @todo Actually test SNDCTL_DSP_CURRENT_IPTR.
1372 	 */
1373 		chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch;
1374 		if (chn == NULL)
1375 			ret = EINVAL;
1376 		else {
1377 			struct snd_dbuf *bs;
1378 			/* int tmp; */
1379 
1380 			oss_count_t *oc = (oss_count_t *)arg;
1381 
1382 			CHN_LOCK(chn);
1383 			bs = chn->bufsoft;
1384 #if 0
1385 			tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b);
1386 			oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getbps(b);
1387 			oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getbps(b);
1388 #else
1389 			oc->samples = sndbuf_gettotal(bs) / sndbuf_getbps(bs);
1390 			oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getbps(bs);
1391 #endif
1392 			CHN_UNLOCK(chn);
1393 		}
1394 		break;
1395 
1396 	case SNDCTL_DSP_HALT_OUTPUT:
1397 	case SNDCTL_DSP_HALT_INPUT:
1398 		chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch;
1399 		if (chn == NULL)
1400 			ret = EINVAL;
1401 		else {
1402 			CHN_LOCK(chn);
1403 			chn_abort(chn);
1404 			CHN_UNLOCK(chn);
1405 		}
1406 		break;
1407 
1408 	case SNDCTL_DSP_LOW_WATER:
1409 	/*
1410 	 * Set the number of bytes required to attract attention by
1411 	 * select/poll.
1412 	 */
1413 		if (wrch != NULL) {
1414 			CHN_LOCK(wrch);
1415 			wrch->lw = (*arg_i > 1) ? *arg_i : 1;
1416 			CHN_UNLOCK(wrch);
1417 		}
1418 		if (rdch != NULL) {
1419 			CHN_LOCK(rdch);
1420 			rdch->lw = (*arg_i > 1) ? *arg_i : 1;
1421 			CHN_UNLOCK(rdch);
1422 		}
1423 		break;
1424 
1425 	case SNDCTL_DSP_GETERROR:
1426 	/*
1427 	 * OSSv4 docs:  "All errors and counters will automatically be
1428 	 * cleared to zeroes after the call so each call will return only
1429 	 * the errors that occurred after the previous invocation. ... The
1430 	 * play_underruns and rec_overrun fields are the only usefull fields
1431 	 * returned by OSS 4.0."
1432 	 */
1433 		{
1434 			audio_errinfo *ei = (audio_errinfo *)arg;
1435 
1436 			bzero((void *)ei, sizeof(*ei));
1437 
1438 			if (wrch != NULL) {
1439 				CHN_LOCK(wrch);
1440 				ei->play_underruns = wrch->xruns;
1441 				wrch->xruns = 0;
1442 				CHN_UNLOCK(wrch);
1443 			}
1444 			if (rdch != NULL) {
1445 				CHN_LOCK(rdch);
1446 				ei->rec_overruns = rdch->xruns;
1447 				rdch->xruns = 0;
1448 				CHN_UNLOCK(rdch);
1449 			}
1450 		}
1451 		break;
1452 
1453 	case SNDCTL_DSP_SYNCGROUP:
1454 		ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg);
1455 		break;
1456 
1457 	case SNDCTL_DSP_SYNCSTART:
1458 		ret = dsp_oss_syncstart(*arg_i);
1459 		break;
1460 
1461 	case SNDCTL_DSP_POLICY:
1462 		ret = dsp_oss_policy(wrch, rdch, *arg_i);
1463 		break;
1464 
1465 #ifdef	OSSV4_EXPERIMENT
1466 	/*
1467 	 * XXX The following ioctls are not yet supported and just return
1468 	 * EINVAL.
1469 	 */
1470 	case SNDCTL_DSP_GETOPEAKS:
1471 	case SNDCTL_DSP_GETIPEAKS:
1472 		chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch;
1473 		if (chn == NULL)
1474 			ret = EINVAL;
1475 		else {
1476 			oss_peaks_t *op = (oss_peaks_t *)arg;
1477 			int lpeak, rpeak;
1478 
1479 			CHN_LOCK(chn);
1480 			ret = chn_getpeaks(chn, &lpeak, &rpeak);
1481 			if (ret == -1)
1482 				ret = EINVAL;
1483 			else {
1484 				(*op)[0] = lpeak;
1485 				(*op)[1] = rpeak;
1486 			}
1487 			CHN_UNLOCK(chn);
1488 		}
1489 		break;
1490 
1491 	case SNDCTL_DSP_COOKEDMODE:
1492 		ret = dsp_oss_cookedmode(wrch, rdch, *arg_i);
1493 		break;
1494 	case SNDCTL_DSP_GET_CHNORDER:
1495 		ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg);
1496 		break;
1497 	case SNDCTL_DSP_SET_CHNORDER:
1498 		ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg);
1499 		break;
1500 	case SNDCTL_GETLABEL:
1501 		ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg);
1502 		break;
1503 	case SNDCTL_SETLABEL:
1504 		ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg);
1505 		break;
1506 	case SNDCTL_GETSONG:
1507 		ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg);
1508 		break;
1509 	case SNDCTL_SETSONG:
1510 		ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg);
1511 		break;
1512 	case SNDCTL_SETNAME:
1513 		ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg);
1514 		break;
1515 #if 0
1516 	/**
1517 	 * @note The SNDCTL_CARDINFO ioctl was omitted per 4Front developer
1518 	 * documentation.  "The usability of this call is very limited. It's
1519 	 * provided only for completeness of the API. OSS API doesn't have
1520 	 * any concept of card. Any information returned by this ioctl calld
1521 	 * is reserved exclusively for the utility programs included in the
1522 	 * OSS package. Applications should not try to use for this
1523 	 * information in any ways."
1524 	 */
1525 	case SNDCTL_CARDINFO:
1526 		ret = EINVAL;
1527 		break;
1528 	/**
1529 	 * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and
1530 	 * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of
1531 	 * 4Front Technologies.
1532 	 */
1533 	case SNDCTL_DSP_READCTL:
1534 	case SNDCTL_DSP_WRITECTL:
1535 		ret = EINVAL;
1536 		break;
1537 #endif	/* !0 (explicitly omitted ioctls) */
1538 
1539 #endif	/* !OSSV4_EXPERIMENT */
1540     	case SNDCTL_DSP_MAPINBUF:
1541     	case SNDCTL_DSP_MAPOUTBUF:
1542     	case SNDCTL_DSP_SETSYNCRO:
1543 		/* undocumented */
1544 
1545     	case SNDCTL_DSP_SUBDIVIDE:
1546     	case SOUND_PCM_WRITE_FILTER:
1547     	case SOUND_PCM_READ_FILTER:
1548 		/* dunno what these do, don't sound important */
1549 
1550     	default:
1551 		DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
1552 		ret = EINVAL;
1553 		break;
1554     	}
1555 	relchns(i_dev, rdch, wrch, 0);
1556     	return ret;
1557 }
1558 
1559 static int
1560 dsp_poll(struct cdev *i_dev, int events, struct thread *td)
1561 {
1562 	struct pcm_channel *wrch = NULL, *rdch = NULL;
1563 	int ret, e;
1564 
1565 	ret = 0;
1566 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1567 
1568 	if (wrch) {
1569 		e = (events & (POLLOUT | POLLWRNORM));
1570 		if (e)
1571 			ret |= chn_poll(wrch, e, td);
1572 	}
1573 	if (rdch) {
1574 		e = (events & (POLLIN | POLLRDNORM));
1575 		if (e)
1576 			ret |= chn_poll(rdch, e, td);
1577 	}
1578 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1579 
1580 	return ret;
1581 }
1582 
1583 static int
1584 dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
1585 {
1586 	struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1587 
1588 	if (nprot & PROT_EXEC)
1589 		return -1;
1590 
1591 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1592 #if 0
1593 	/*
1594 	 * XXX the linux api uses the nprot to select read/write buffer
1595 	 * our vm system doesn't allow this, so force write buffer
1596 	 */
1597 
1598 	if (wrch && (nprot & PROT_WRITE)) {
1599 		c = wrch;
1600 	} else if (rdch && (nprot & PROT_READ)) {
1601 		c = rdch;
1602 	} else {
1603 		return -1;
1604 	}
1605 #else
1606 	c = wrch;
1607 #endif
1608 
1609 	if (c == NULL) {
1610 		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1611 		return -1;
1612 	}
1613 
1614 	if (offset >= sndbuf_getsize(c->bufsoft)) {
1615 		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1616 		return -1;
1617 	}
1618 
1619 	if (!(c->flags & CHN_F_MAPPED))
1620 		c->flags |= CHN_F_MAPPED;
1621 
1622 	*paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
1623 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1624 
1625 	return 0;
1626 }
1627 
1628 #ifdef USING_DEVFS
1629 
1630 /* So much for dev_stdclone() */
1631 static int
1632 dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c)
1633 {
1634 	size_t len;
1635 
1636 	len = strlen(namep);
1637 
1638 	if (bcmp(name, namep, len) != 0)
1639 		return (ENODEV);
1640 
1641 	name += len;
1642 
1643 	if (isdigit(*name) == 0)
1644 		return (ENODEV);
1645 
1646 	len = strlen(sep);
1647 
1648 	if (*name == '0' && !(name[1] == '\0' || bcmp(name + 1, sep, len) == 0))
1649 		return (ENODEV);
1650 
1651 	for (*u = 0; isdigit(*name) != 0; name++) {
1652 		*u *= 10;
1653 		*u += *name - '0';
1654 		if (*u > dsp_umax)
1655 			return (ENODEV);
1656 	}
1657 
1658 	if (*name == '\0')
1659 		return ((use_sep == 0) ? 0 : ENODEV);
1660 
1661 	if (bcmp(name, sep, len) != 0 || isdigit(name[len]) == 0)
1662 		return (ENODEV);
1663 
1664 	name += len;
1665 
1666 	if (*name == '0' && name[1] != '\0')
1667 		return (ENODEV);
1668 
1669 	for (*c = 0; isdigit(*name) != 0; name++) {
1670 		*c *= 10;
1671 		*c += *name - '0';
1672 		if (*c > dsp_cmax)
1673 			return (ENODEV);
1674 	}
1675 
1676 	if (*name != '\0')
1677 		return (ENODEV);
1678 
1679 	return (0);
1680 }
1681 
1682 static void
1683 dsp_clone(void *arg,
1684 #if __FreeBSD_version >= 600034
1685     struct ucred *cred,
1686 #endif
1687     char *name, int namelen, struct cdev **dev)
1688 {
1689 	struct snddev_info *d;
1690 	struct snd_clone_entry *ce;
1691 	struct pcm_channel *c;
1692 	int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax;
1693 	char *devname, *devsep;
1694 
1695 	KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!"));
1696 
1697 	if (*dev != NULL)
1698 		return;
1699 
1700 	unit = -1;
1701 	cunit = -1;
1702 	devtype = -1;
1703 	devhw = 0;
1704 	devcmax = -1;
1705 	tumax = -1;
1706 	devname = NULL;
1707 	devsep = NULL;
1708 
1709 	for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])) &&
1710 	    unit == -1; i++) {
1711 		devtype = dsp_cdevs[i].type;
1712 		devname = dsp_cdevs[i].name;
1713 		devsep = dsp_cdevs[i].sep;
1714 		devhw = dsp_cdevs[i].hw;
1715 		devcmax = dsp_cdevs[i].max - 1;
1716 		if (strcmp(name, devname) == 0)
1717 			unit = snd_unit;
1718 		else if (dsp_stdclone(name, devname, devsep,
1719 		    dsp_cdevs[i].use_sep, &unit, &cunit) != 0) {
1720 			unit = -1;
1721 			cunit = -1;
1722 		}
1723 	}
1724 
1725 	d = devclass_get_softc(pcm_devclass, unit);
1726 	if (d == NULL || d->clones == NULL)
1727 		return;
1728 
1729 	pcm_lock(d);
1730 	if (snd_clone_disabled(d->clones)) {
1731 		pcm_unlock(d);
1732 		return;
1733 	}
1734 
1735 	udcmask = snd_u2unit(unit) | snd_d2unit(devtype);
1736 
1737 	if (devhw != 0) {
1738 		KASSERT(devcmax <= dsp_cmax,
1739 		    ("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax));
1740 		if (cunit > devcmax) {
1741 			pcm_unlock(d);
1742 			return;
1743 		}
1744 		udcmask |= snd_c2unit(cunit);
1745 		CHN_FOREACH(c, d, channels.pcm) {
1746 			CHN_LOCK(c);
1747 			if (c->unit != udcmask) {
1748 				CHN_UNLOCK(c);
1749 				continue;
1750 			}
1751 			CHN_UNLOCK(c);
1752 			udcmask &= ~snd_c2unit(cunit);
1753 			/*
1754 			 * Temporarily increase clone maxunit to overcome
1755 			 * vchan flexibility.
1756 			 *
1757 			 * # sysctl dev.pcm.0.play.vchans=256
1758 			 * dev.pcm.0.play.vchans: 1 -> 256
1759 			 * # cat /dev/zero > /dev/dsp0.vp255 &
1760 			 * [1] 17296
1761 			 * # sysctl dev.pcm.0.play.vchans=0
1762 			 * dev.pcm.0.play.vchans: 256 -> 1
1763 			 * # fg
1764 			 * [1]  + running    cat /dev/zero > /dev/dsp0.vp255
1765 			 * ^C
1766 			 * # cat /dev/zero > /dev/dsp0.vp255
1767 			 * zsh: operation not supported: /dev/dsp0.vp255
1768 			 */
1769 			tumax = snd_clone_getmaxunit(d->clones);
1770 			if (cunit > tumax)
1771 				snd_clone_setmaxunit(d->clones, cunit);
1772 			else
1773 				tumax = -1;
1774 			goto dsp_clone_alloc;
1775 		}
1776 		/*
1777 		 * Ok, so we're requesting unallocated vchan, but still
1778 		 * within maximum vchan limit.
1779 		 */
1780 		if (((devtype == SND_DEV_DSPHW_VPLAY && d->pvchancount > 0) ||
1781 		    (devtype == SND_DEV_DSPHW_VREC && d->rvchancount > 0)) &&
1782 		    cunit < snd_maxautovchans) {
1783 			udcmask &= ~snd_c2unit(cunit);
1784 			tumax = snd_clone_getmaxunit(d->clones);
1785 			if (cunit > tumax)
1786 				snd_clone_setmaxunit(d->clones, cunit);
1787 			else
1788 				tumax = -1;
1789 			goto dsp_clone_alloc;
1790 		}
1791 		pcm_unlock(d);
1792 		return;
1793 	}
1794 
1795 dsp_clone_alloc:
1796 	ce = snd_clone_alloc(d->clones, dev, &cunit, udcmask);
1797 	if (tumax != -1)
1798 		snd_clone_setmaxunit(d->clones, tumax);
1799 	if (ce != NULL) {
1800 		udcmask |= snd_c2unit(cunit);
1801 		pcm_unlock(d);
1802 		*dev = make_dev(&dsp_cdevsw, unit2minor(udcmask),
1803 		    UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d",
1804 		    devname, unit, devsep, cunit);
1805 		pcm_lock(d);
1806 		snd_clone_register(ce, *dev);
1807 	}
1808 	pcm_unlock(d);
1809 
1810 	if (*dev != NULL)
1811 		dev_ref(*dev);
1812 }
1813 
1814 static void
1815 dsp_sysinit(void *p)
1816 {
1817 	if (dsp_ehtag != NULL)
1818 		return;
1819 	/* initialize unit numbering */
1820 	snd_unit_init();
1821 	dsp_umax = PCMMAXUNIT;
1822 	dsp_cmax = PCMMAXCHAN;
1823 	dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1824 }
1825 
1826 static void
1827 dsp_sysuninit(void *p)
1828 {
1829 	if (dsp_ehtag == NULL)
1830 		return;
1831 	EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1832 	dsp_ehtag = NULL;
1833 }
1834 
1835 SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1836 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1837 #endif
1838 
1839 char *
1840 dsp_unit2name(char *buf, size_t len, int unit)
1841 {
1842 	int i, dtype;
1843 
1844 	KASSERT(buf != NULL && len != 0,
1845 	    ("bogus buf=%p len=%ju", buf, (uintmax_t)len));
1846 
1847 	dtype = snd_unit2d(unit);
1848 
1849 	for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
1850 		if (dtype != dsp_cdevs[i].type)
1851 			continue;
1852 		snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name,
1853 		    snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit));
1854 		return (buf);
1855 	}
1856 
1857 	return (NULL);
1858 }
1859 
1860 /**
1861  * @brief Handler for SNDCTL_AUDIOINFO.
1862  *
1863  * Gathers information about the audio device specified in ai->dev.  If
1864  * ai->dev == -1, then this function gathers information about the current
1865  * device.  If the call comes in on a non-audio device and ai->dev == -1,
1866  * return EINVAL.
1867  *
1868  * This routine is supposed to go practically straight to the hardware,
1869  * getting capabilities directly from the sound card driver, side-stepping
1870  * the intermediate channel interface.
1871  *
1872  * Note, however, that the usefulness of this command is significantly
1873  * decreased when requesting info about any device other than the one serving
1874  * the request. While each snddev_channel refers to a specific device node,
1875  * the converse is *not* true.  Currently, when a sound device node is opened,
1876  * the sound subsystem scans for an available audio channel (or channels, if
1877  * opened in read+write) and then assigns them to the si_drv[12] private
1878  * data fields.  As a result, any information returned linking a channel to
1879  * a specific character device isn't necessarily accurate.
1880  *
1881  * @note
1882  * Calling threads must not hold any snddev_info or pcm_channel locks.
1883  *
1884  * @param dev		device on which the ioctl was issued
1885  * @param ai		ioctl request data container
1886  *
1887  * @retval 0		success
1888  * @retval EINVAL	ai->dev specifies an invalid device
1889  *
1890  * @todo Verify correctness of Doxygen tags.  ;)
1891  */
1892 int
1893 dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
1894 {
1895 	struct pcmchan_caps *caps;
1896 	struct pcm_channel *ch;
1897 	struct snddev_info *d;
1898 	uint32_t fmts;
1899 	int i, nchan, ret, *rates, minch, maxch;
1900 	char *devname, buf[CHN_NAMELEN];
1901 
1902 	/*
1903 	 * If probing the device that received the ioctl, make sure it's a
1904 	 * DSP device.  (Users may use this ioctl with /dev/mixer and
1905 	 * /dev/midi.)
1906 	 */
1907 	if ((ai->dev == -1) && (i_dev->si_devsw != &dsp_cdevsw))
1908 		return EINVAL;
1909 
1910 	ch = NULL;
1911 	devname = NULL;
1912 	nchan = 0;
1913 	ret = 0;
1914 	bzero(buf, sizeof(buf));
1915 
1916 	/*
1917 	 * Search for the requested audio device (channel).  Start by
1918 	 * iterating over pcm devices.
1919 	 */
1920 	for (i = 0; pcm_devclass != NULL &&
1921 	    i < devclass_get_maxunit(pcm_devclass); i++) {
1922 		d = devclass_get_softc(pcm_devclass, i);
1923 		if (d == NULL)
1924 			continue;
1925 
1926 		/* See the note in function docblock */
1927 		mtx_assert(d->lock, MA_NOTOWNED);
1928 		pcm_inprog(d, 1);
1929 		pcm_lock(d);
1930 
1931 		CHN_FOREACH(ch, d, channels.pcm) {
1932 			mtx_assert(ch->lock, MA_NOTOWNED);
1933 			CHN_LOCK(ch);
1934 			if (ai->dev == -1) {
1935 				if ((ch == PCM_RDCH(i_dev)) ||	/* record ch */
1936 				    (ch == PCM_WRCH(i_dev))) {	/* playback ch */
1937 					devname = i_dev->si_name;
1938 					goto dspfound;
1939 				}
1940 			} else if (ai->dev == nchan) {
1941 				devname = dsp_unit2name(buf, sizeof(buf),
1942 				    ch->unit);
1943 				goto dspfound;
1944 			}
1945 			CHN_UNLOCK(ch);
1946 			/*
1947 			 * XXX I really doubt if this is correct.
1948 			 */
1949 			++nchan;
1950 		}
1951 
1952 		pcm_unlock(d);
1953 		pcm_inprog(d, -1);
1954 	}
1955 
1956 	/* Exhausted the search -- nothing is locked, so return. */
1957 	return EINVAL;
1958 
1959 dspfound:
1960 	/* Should've found the device, but something isn't right */
1961 	if (devname == NULL) {
1962 		ret = EINVAL;
1963 		goto out;
1964 	}
1965 
1966 	/*
1967 	 * At this point, the following synchronization stuff has happened:
1968 	 *   - a specific PCM device is locked and its "in progress
1969 	 *     operations" counter has been incremented, so be sure to unlock
1970 	 *     and decrement when exiting;
1971 	 *   - a specific audio channel has been locked, so be sure to unlock
1972 	 *     when exiting;
1973 	 */
1974 
1975 	caps = chn_getcaps(ch);
1976 
1977 	/*
1978 	 * With all handles collected, zero out the user's container and
1979 	 * begin filling in its fields.
1980 	 */
1981 	bzero((void *)ai, sizeof(oss_audioinfo));
1982 
1983 	ai->dev = nchan;
1984 	strlcpy(ai->name, ch->name,  sizeof(ai->name));
1985 
1986 	if ((ch->flags & CHN_F_BUSY) == 0)
1987 		ai->busy = 0;
1988 	else
1989 		ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
1990 
1991 	/**
1992 	 * @note
1993 	 * @c cmd - OSSv4 docs: "Only supported under Linux at this moment."
1994 	 * 	Cop-out, I know, but I'll save running around in the process
1995 	 * 	table for later.  Is there a risk of leaking information?
1996 	 */
1997 	ai->pid = ch->pid;
1998 
1999 	/*
2000 	 * These flags stolen from SNDCTL_DSP_GETCAPS handler.  Note, however,
2001 	 * that a single channel operates in only one direction, so
2002 	 * DSP_CAP_DUPLEX is out.
2003 	 */
2004 	/**
2005 	 * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep these in
2006 	 * 	 pcmchan::caps?
2007 	 */
2008 	ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
2009 
2010 	/*
2011 	 * Collect formats supported @b natively by the device.  Also
2012 	 * determine min/max channels.  (I.e., mono, stereo, or both?)
2013 	 *
2014 	 * If any channel is stereo, maxch = 2;
2015 	 * if all channels are stereo, minch = 2, too;
2016 	 * if any channel is mono, minch = 1;
2017 	 * and if all channels are mono, maxch = 1.
2018 	 */
2019 	minch = 0;
2020 	maxch = 0;
2021 	fmts = 0;
2022 	for (i = 0; caps->fmtlist[i]; i++) {
2023 		fmts |= caps->fmtlist[i];
2024 		if (caps->fmtlist[i] & AFMT_STEREO) {
2025 			minch = (minch == 0) ? 2 : minch;
2026 			maxch = 2;
2027 		} else {
2028 			minch = 1;
2029 			maxch = (maxch == 0) ? 1 : maxch;
2030 		}
2031 	}
2032 
2033 	if (ch->direction == PCMDIR_PLAY)
2034 		ai->oformats = fmts;
2035 	else
2036 		ai->iformats = fmts;
2037 
2038 	/**
2039 	 * @note
2040 	 * @c magic - OSSv4 docs: "Reserved for internal use by OSS."
2041 	 *
2042 	 * @par
2043 	 * @c card_number - OSSv4 docs: "Number of the sound card where this
2044 	 * 	device belongs or -1 if this information is not available.
2045 	 * 	Applications should normally not use this field for any
2046 	 * 	purpose."
2047 	 */
2048 	ai->card_number = -1;
2049 	/**
2050 	 * @todo @c song_name - depends first on SNDCTL_[GS]ETSONG
2051 	 * @todo @c label - depends on SNDCTL_[GS]ETLABEL
2052 	 * @todo @c port_number - routing information?
2053 	 */
2054 	ai->port_number = -1;
2055 	ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
2056 	/**
2057 	 * @note
2058 	 * @c real_device - OSSv4 docs:  "Obsolete."
2059 	 */
2060 	ai->real_device = -1;
2061 	strlcpy(ai->devnode, devname, sizeof(ai->devnode));
2062 	ai->enabled = device_is_attached(d->dev) ? 1 : 0;
2063 	/**
2064 	 * @note
2065 	 * @c flags - OSSv4 docs: "Reserved for future use."
2066 	 *
2067 	 * @note
2068 	 * @c binding - OSSv4 docs: "Reserved for future use."
2069 	 *
2070 	 * @todo @c handle - haven't decided how to generate this yet; bus,
2071 	 * 	vendor, device IDs?
2072 	 */
2073 	ai->min_rate = caps->minspeed;
2074 	ai->max_rate = caps->maxspeed;
2075 
2076 	ai->min_channels = minch;
2077 	ai->max_channels = maxch;
2078 
2079 	ai->nrates = chn_getrates(ch, &rates);
2080 	if (ai->nrates > OSS_MAX_SAMPLE_RATES)
2081 		ai->nrates = OSS_MAX_SAMPLE_RATES;
2082 
2083 	for (i = 0; i < ai->nrates; i++)
2084 		ai->rates[i] = rates[i];
2085 
2086 out:
2087 	CHN_UNLOCK(ch);
2088 	pcm_unlock(d);
2089 	pcm_inprog(d, -1);
2090 
2091 	return ret;
2092 }
2093 
2094 /**
2095  * @brief Assigns a PCM channel to a sync group.
2096  *
2097  * Sync groups are used to enable audio operations on multiple devices
2098  * simultaneously.  They may be used with any number of devices and may
2099  * span across applications.  Devices are added to groups with
2100  * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the
2101  * SNDCTL_DSP_SYNCSTART ioctl.
2102  *
2103  * If the @c id field of the @c group parameter is set to zero, then a new
2104  * sync group is created.  Otherwise, wrch and rdch (if set) are added to
2105  * the group specified.
2106  *
2107  * @todo As far as memory allocation, should we assume that things are
2108  * 	 okay and allocate with M_WAITOK before acquiring channel locks,
2109  * 	 freeing later if not?
2110  *
2111  * @param wrch	output channel associated w/ device (if any)
2112  * @param rdch	input channel associated w/ device (if any)
2113  * @param group Sync group parameters
2114  *
2115  * @retval 0		success
2116  * @retval non-zero	error to be propagated upstream
2117  */
2118 static int
2119 dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group)
2120 {
2121 	struct pcmchan_syncmember *smrd, *smwr;
2122 	struct pcmchan_syncgroup *sg;
2123 	int ret, sg_ids[3];
2124 
2125 	smrd = NULL;
2126 	smwr = NULL;
2127 	sg = NULL;
2128 	ret = 0;
2129 
2130 	/*
2131 	 * Free_unr() may sleep, so store released syncgroup IDs until after
2132 	 * all locks are released.
2133 	 */
2134 	sg_ids[0] = sg_ids[1] = sg_ids[2] = 0;
2135 
2136 	PCM_SG_LOCK();
2137 
2138 	/*
2139 	 * - Insert channel(s) into group's member list.
2140 	 * - Set CHN_F_NOTRIGGER on channel(s).
2141 	 * - Stop channel(s).
2142 	 */
2143 
2144 	/*
2145 	 * If device's channels are already mapped to a group, unmap them.
2146 	 */
2147 	if (wrch) {
2148 		CHN_LOCK(wrch);
2149 		sg_ids[0] = chn_syncdestroy(wrch);
2150 	}
2151 
2152 	if (rdch) {
2153 		CHN_LOCK(rdch);
2154 		sg_ids[1] = chn_syncdestroy(rdch);
2155 	}
2156 
2157 	/*
2158 	 * Verify that mode matches character device properites.
2159 	 *  - Bail if PCM_ENABLE_OUTPUT && wrch == NULL.
2160 	 *  - Bail if PCM_ENABLE_INPUT && rdch == NULL.
2161 	 */
2162 	if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) ||
2163 	    ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) {
2164 		ret = EINVAL;
2165 		goto out;
2166 	}
2167 
2168 	/*
2169 	 * An id of zero indicates the user wants to create a new
2170 	 * syncgroup.
2171 	 */
2172 	if (group->id == 0) {
2173 		sg = (struct pcmchan_syncgroup *)malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT);
2174 		if (sg != NULL) {
2175 			SLIST_INIT(&sg->members);
2176 			sg->id = alloc_unr(pcmsg_unrhdr);
2177 
2178 			group->id = sg->id;
2179 			SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link);
2180 		} else
2181 			ret = ENOMEM;
2182 	} else {
2183 		SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
2184 			if (sg->id == group->id)
2185 				break;
2186 		}
2187 		if (sg == NULL)
2188 			ret = EINVAL;
2189 	}
2190 
2191 	/* Couldn't create or find a syncgroup.  Fail. */
2192 	if (sg == NULL)
2193 		goto out;
2194 
2195 	/*
2196 	 * Allocate a syncmember, assign it and a channel together, and
2197 	 * insert into syncgroup.
2198 	 */
2199 	if (group->mode & PCM_ENABLE_INPUT) {
2200 		smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
2201 		if (smrd == NULL) {
2202 			ret = ENOMEM;
2203 			goto out;
2204 		}
2205 
2206 		SLIST_INSERT_HEAD(&sg->members, smrd, link);
2207 		smrd->parent = sg;
2208 		smrd->ch = rdch;
2209 
2210 		chn_abort(rdch);
2211 		rdch->flags |= CHN_F_NOTRIGGER;
2212 		rdch->sm = smrd;
2213 	}
2214 
2215 	if (group->mode & PCM_ENABLE_OUTPUT) {
2216 		smwr = (struct pcmchan_syncmember *)malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT);
2217 		if (smwr == NULL) {
2218 			ret = ENOMEM;
2219 			goto out;
2220 		}
2221 
2222 		SLIST_INSERT_HEAD(&sg->members, smwr, link);
2223 		smwr->parent = sg;
2224 		smwr->ch = wrch;
2225 
2226 		chn_abort(wrch);
2227 		wrch->flags |= CHN_F_NOTRIGGER;
2228 		wrch->sm = smwr;
2229 	}
2230 
2231 
2232 out:
2233 	if (ret != 0) {
2234 		if (smrd != NULL)
2235 			free(smrd, M_DEVBUF);
2236 		if ((sg != NULL) && SLIST_EMPTY(&sg->members)) {
2237 			sg_ids[2] = sg->id;
2238 			SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
2239 			free(sg, M_DEVBUF);
2240 		}
2241 
2242 		if (wrch)
2243 			wrch->sm = NULL;
2244 		if (rdch)
2245 			rdch->sm = NULL;
2246 	}
2247 
2248 	if (wrch)
2249 		CHN_UNLOCK(wrch);
2250 	if (rdch)
2251 		CHN_UNLOCK(rdch);
2252 
2253 	PCM_SG_UNLOCK();
2254 
2255 	if (sg_ids[0])
2256 		free_unr(pcmsg_unrhdr, sg_ids[0]);
2257 	if (sg_ids[1])
2258 		free_unr(pcmsg_unrhdr, sg_ids[1]);
2259 	if (sg_ids[2])
2260 		free_unr(pcmsg_unrhdr, sg_ids[2]);
2261 
2262 	return ret;
2263 }
2264 
2265 /**
2266  * @brief Launch a sync group into action
2267  *
2268  * Sync groups are established via SNDCTL_DSP_SYNCGROUP.  This function
2269  * iterates over all members, triggering them along the way.
2270  *
2271  * @note Caller must not hold any channel locks.
2272  *
2273  * @param sg_id	sync group identifier
2274  *
2275  * @retval 0	success
2276  * @retval non-zero	error worthy of propagating upstream to user
2277  */
2278 static int
2279 dsp_oss_syncstart(int sg_id)
2280 {
2281 	struct pcmchan_syncmember *sm, *sm_tmp;
2282 	struct pcmchan_syncgroup *sg;
2283 	struct pcm_channel *c;
2284 	int ret, needlocks;
2285 
2286 	/* Get the synclists lock */
2287 	PCM_SG_LOCK();
2288 
2289 	do {
2290 		ret = 0;
2291 		needlocks = 0;
2292 
2293 		/* Search for syncgroup by ID */
2294 		SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) {
2295 			if (sg->id == sg_id)
2296 				break;
2297 		}
2298 
2299 		/* Return EINVAL if not found */
2300 		if (sg == NULL) {
2301 			ret = EINVAL;
2302 			break;
2303 		}
2304 
2305 		/* Any removals resulting in an empty group should've handled this */
2306 		KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup"));
2307 
2308 		/*
2309 		 * Attempt to lock all member channels - if any are already
2310 		 * locked, unlock those acquired, sleep for a bit, and try
2311 		 * again.
2312 		 */
2313 		SLIST_FOREACH(sm, &sg->members, link) {
2314 			if (CHN_TRYLOCK(sm->ch) == 0) {
2315 				int timo = hz * 5/1000;
2316 				if (timo < 1)
2317 					timo = 1;
2318 
2319 				/* Release all locked channels so far, retry */
2320 				SLIST_FOREACH(sm_tmp, &sg->members, link) {
2321 					/* sm is the member already locked */
2322 					if (sm == sm_tmp)
2323 						break;
2324 					CHN_UNLOCK(sm_tmp->ch);
2325 				}
2326 
2327 				/** @todo Is PRIBIO correct/ */
2328 				ret = msleep(sm, &snd_pcm_syncgroups_mtx, PRIBIO | PCATCH, "pcmsgrp", timo);
2329 				if (ret == EINTR || ret == ERESTART)
2330 					break;
2331 
2332 				needlocks = 1;
2333 				ret = 0; /* Assumes ret == EAGAIN... */
2334 			}
2335 		}
2336 	} while (needlocks && ret == 0);
2337 
2338 	/* Proceed only if no errors encountered. */
2339 	if (ret == 0) {
2340 		/* Launch channels */
2341 		while((sm = SLIST_FIRST(&sg->members)) != NULL) {
2342 			SLIST_REMOVE_HEAD(&sg->members, link);
2343 
2344 			c = sm->ch;
2345 			c->sm = NULL;
2346 			chn_start(c, 1);
2347 			c->flags &= ~CHN_F_NOTRIGGER;
2348 			CHN_UNLOCK(c);
2349 
2350 			free(sm, M_DEVBUF);
2351 		}
2352 
2353 		SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
2354 		free(sg, M_DEVBUF);
2355 	}
2356 
2357 	PCM_SG_UNLOCK();
2358 
2359 	/*
2360 	 * Free_unr() may sleep, so be sure to give up the syncgroup lock
2361 	 * first.
2362 	 */
2363 	if (ret == 0)
2364 		free_unr(pcmsg_unrhdr, sg_id);
2365 
2366 	return ret;
2367 }
2368 
2369 /**
2370  * @brief Handler for SNDCTL_DSP_POLICY
2371  *
2372  * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment
2373  * size and count like with SNDCTL_DSP_SETFRAGMENT.  Instead of the user
2374  * specifying those two parameters, s/he simply selects a number from 0..10
2375  * which corresponds to a buffer size.  Smaller numbers request smaller
2376  * buffers with lower latencies (at greater overhead from more frequent
2377  * interrupts), while greater numbers behave in the opposite manner.
2378  *
2379  * The 4Front spec states that a value of 5 should be the default.  However,
2380  * this implementation deviates slightly by using a linear scale without
2381  * consulting drivers.  I.e., even though drivers may have different default
2382  * buffer sizes, a policy argument of 5 will have the same result across
2383  * all drivers.
2384  *
2385  * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for
2386  * more information.
2387  *
2388  * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to
2389  * 	 work with hardware drivers directly.
2390  *
2391  * @note PCM channel arguments must not be locked by caller.
2392  *
2393  * @param wrch	Pointer to opened playback channel (optional; may be NULL)
2394  * @param rdch	" recording channel (optional; may be NULL)
2395  * @param policy Integer from [0:10]
2396  *
2397  * @retval 0	constant (for now)
2398  */
2399 static int
2400 dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
2401 {
2402 	int ret;
2403 
2404 	if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX)
2405 		return EIO;
2406 
2407 	/* Default: success */
2408 	ret = 0;
2409 
2410 	if (rdch) {
2411 		CHN_LOCK(rdch);
2412 		ret = chn_setlatency(rdch, policy);
2413 		CHN_UNLOCK(rdch);
2414 	}
2415 
2416 	if (wrch && ret == 0) {
2417 		CHN_LOCK(wrch);
2418 		ret = chn_setlatency(wrch, policy);
2419 		CHN_UNLOCK(wrch);
2420 	}
2421 
2422 	if (ret)
2423 		ret = EIO;
2424 
2425 	return ret;
2426 }
2427 
2428 #ifdef OSSV4_EXPERIMENT
2429 /**
2430  * @brief Enable or disable "cooked" mode
2431  *
2432  * This is a handler for @c SNDCTL_DSP_COOKEDMODE.  When in cooked mode, which
2433  * is the default, the sound system handles rate and format conversions
2434  * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only
2435  * operates with 44100Hz/16bit/signed samples).
2436  *
2437  * Disabling cooked mode is intended for applications wanting to mmap()
2438  * a sound card's buffer space directly, bypassing the FreeBSD 2-stage
2439  * feeder architecture, presumably to gain as much control over audio
2440  * hardware as possible.
2441  *
2442  * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html
2443  * for more details.
2444  *
2445  * @note Currently, this function is just a stub that always returns EINVAL.
2446  *
2447  * @todo Figure out how to and actually implement this.
2448  *
2449  * @param wrch		playback channel (optional; may be NULL)
2450  * @param rdch		recording channel (optional; may be NULL)
2451  * @param enabled	0 = raw mode, 1 = cooked mode
2452  *
2453  * @retval EINVAL	Operation not yet supported.
2454  */
2455 static int
2456 dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled)
2457 {
2458 	return EINVAL;
2459 }
2460 
2461 /**
2462  * @brief Retrieve channel interleaving order
2463  *
2464  * This is the handler for @c SNDCTL_DSP_GET_CHNORDER.
2465  *
2466  * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html
2467  * for more details.
2468  *
2469  * @note As the ioctl definition is still under construction, FreeBSD
2470  * 	 does not currently support SNDCTL_DSP_GET_CHNORDER.
2471  *
2472  * @param wrch	playback channel (optional; may be NULL)
2473  * @param rdch	recording channel (optional; may be NULL)
2474  * @param map	channel map (result will be stored there)
2475  *
2476  * @retval EINVAL	Operation not yet supported.
2477  */
2478 static int
2479 dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
2480 {
2481 	return EINVAL;
2482 }
2483 
2484 /**
2485  * @brief Specify channel interleaving order
2486  *
2487  * This is the handler for @c SNDCTL_DSP_SET_CHNORDER.
2488  *
2489  * @note As the ioctl definition is still under construction, FreeBSD
2490  * 	 does not currently support @c SNDCTL_DSP_SET_CHNORDER.
2491  *
2492  * @param wrch	playback channel (optional; may be NULL)
2493  * @param rdch	recording channel (optional; may be NULL)
2494  * @param map	channel map
2495  *
2496  * @retval EINVAL	Operation not yet supported.
2497  */
2498 static int
2499 dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
2500 {
2501 	return EINVAL;
2502 }
2503 
2504 /**
2505  * @brief Retrieve an audio device's label
2506  *
2507  * This is a handler for the @c SNDCTL_GETLABEL ioctl.
2508  *
2509  * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
2510  * for more details.
2511  *
2512  * From Hannu@4Front:  "For example ossxmix (just like some HW mixer
2513  * consoles) can show variable "labels" for certain controls. By default
2514  * the application name (say quake) is shown as the label but
2515  * applications may change the labels themselves."
2516  *
2517  * @note As the ioctl definition is still under construction, FreeBSD
2518  * 	 does not currently support @c SNDCTL_GETLABEL.
2519  *
2520  * @param wrch	playback channel (optional; may be NULL)
2521  * @param rdch	recording channel (optional; may be NULL)
2522  * @param label	label gets copied here
2523  *
2524  * @retval EINVAL	Operation not yet supported.
2525  */
2526 static int
2527 dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
2528 {
2529 	return EINVAL;
2530 }
2531 
2532 /**
2533  * @brief Specify an audio device's label
2534  *
2535  * This is a handler for the @c SNDCTL_SETLABEL ioctl.  Please see the
2536  * comments for @c dsp_oss_getlabel immediately above.
2537  *
2538  * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html
2539  * for more details.
2540  *
2541  * @note As the ioctl definition is still under construction, FreeBSD
2542  * 	 does not currently support SNDCTL_SETLABEL.
2543  *
2544  * @param wrch	playback channel (optional; may be NULL)
2545  * @param rdch	recording channel (optional; may be NULL)
2546  * @param label	label gets copied from here
2547  *
2548  * @retval EINVAL	Operation not yet supported.
2549  */
2550 static int
2551 dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
2552 {
2553 	return EINVAL;
2554 }
2555 
2556 /**
2557  * @brief Retrieve name of currently played song
2558  *
2559  * This is a handler for the @c SNDCTL_GETSONG ioctl.  Audio players could
2560  * tell the system the name of the currently playing song, which would be
2561  * visible in @c /dev/sndstat.
2562  *
2563  * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html
2564  * for more details.
2565  *
2566  * @note As the ioctl definition is still under construction, FreeBSD
2567  * 	 does not currently support SNDCTL_GETSONG.
2568  *
2569  * @param wrch	playback channel (optional; may be NULL)
2570  * @param rdch	recording channel (optional; may be NULL)
2571  * @param song	song name gets copied here
2572  *
2573  * @retval EINVAL	Operation not yet supported.
2574  */
2575 static int
2576 dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
2577 {
2578 	return EINVAL;
2579 }
2580 
2581 /**
2582  * @brief Retrieve name of currently played song
2583  *
2584  * This is a handler for the @c SNDCTL_SETSONG ioctl.  Audio players could
2585  * tell the system the name of the currently playing song, which would be
2586  * visible in @c /dev/sndstat.
2587  *
2588  * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html
2589  * for more details.
2590  *
2591  * @note As the ioctl definition is still under construction, FreeBSD
2592  * 	 does not currently support SNDCTL_SETSONG.
2593  *
2594  * @param wrch	playback channel (optional; may be NULL)
2595  * @param rdch	recording channel (optional; may be NULL)
2596  * @param song	song name gets copied from here
2597  *
2598  * @retval EINVAL	Operation not yet supported.
2599  */
2600 static int
2601 dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
2602 {
2603 	return EINVAL;
2604 }
2605 
2606 /**
2607  * @brief Rename a device
2608  *
2609  * This is a handler for the @c SNDCTL_SETNAME ioctl.
2610  *
2611  * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for
2612  * more details.
2613  *
2614  * From Hannu@4Front:  "This call is used to change the device name
2615  * reported in /dev/sndstat and ossinfo. So instead of  using some generic
2616  * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull
2617  * name depending on the current context (for example 'OSS virtual wave table
2618  * synth' or 'VoIP link to London')."
2619  *
2620  * @note As the ioctl definition is still under construction, FreeBSD
2621  * 	 does not currently support SNDCTL_SETNAME.
2622  *
2623  * @param wrch	playback channel (optional; may be NULL)
2624  * @param rdch	recording channel (optional; may be NULL)
2625  * @param name	new device name gets copied from here
2626  *
2627  * @retval EINVAL	Operation not yet supported.
2628  */
2629 static int
2630 dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name)
2631 {
2632 	return EINVAL;
2633 }
2634 #endif	/* !OSSV4_EXPERIMENT */
2635