xref: /dflybsd-src/sys/dev/sound/pcm/dsp.c (revision 3546e044efc2789e203cdc14abe0226e163e03d4)
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  * $FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.80.2.6 2006/04/04 17:43:48 ariff Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.17 2008/02/28 17:19:11 tgen Exp $
28  */
29 
30 #include <sys/param.h>
31 #include <sys/queue.h>
32 #include <sys/event.h>
33 
34 #include <dev/sound/pcm/dsp.h>
35 #include <dev/sound/pcm/sound.h>
36 
37 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.17 2008/02/28 17:19:11 tgen Exp $");
38 
39 #define OLDPCM_IOCTL
40 
41 static d_open_t dsp_open;
42 static d_close_t dsp_close;
43 static d_read_t dsp_read;
44 static d_write_t dsp_write;
45 static d_ioctl_t dsp_ioctl;
46 static d_poll_t dsp_poll;
47 static d_kqfilter_t dsp_kqfilter;
48 static d_mmap_t dsp_mmap;
49 
50 static void dsp_filter_detach(struct knote *);
51 static int dsp_filter_read(struct knote *, long);
52 static int dsp_filter_write(struct knote *, long);
53 
54 struct dev_ops dsp_cdevsw = {
55 	{ "dsp", SND_CDEV_MAJOR, 0},
56 	/*.d_flags =	D_NEEDGIANT,*/
57 	.d_open =	dsp_open,
58 	.d_close =	dsp_close,
59 	.d_read =	dsp_read,
60 	.d_write =	dsp_write,
61 	.d_ioctl =	dsp_ioctl,
62 	.d_poll =	dsp_poll,
63 	.d_kqfilter =	dsp_kqfilter,
64 	.d_mmap =	dsp_mmap,
65 };
66 
67 struct snddev_info *
68 dsp_get_info(struct cdev *dev)
69 {
70 	struct snddev_info *d;
71 	int unit;
72 
73 	unit = PCMUNIT(dev);
74 	if (unit >= devclass_get_maxunit(pcm_devclass))
75 		return NULL;
76 	d = devclass_get_softc(pcm_devclass, unit);
77 
78 	return d;
79 }
80 
81 static u_int32_t
82 dsp_get_flags(struct cdev *dev)
83 {
84 	device_t bdev;
85 	int unit;
86 
87 	unit = PCMUNIT(dev);
88 	if (unit >= devclass_get_maxunit(pcm_devclass))
89 		return 0xffffffff;
90 	bdev = devclass_get_device(pcm_devclass, unit);
91 
92 	return pcm_getflags(bdev);
93 }
94 
95 static void
96 dsp_set_flags(struct cdev *dev, u_int32_t flags)
97 {
98 	device_t bdev;
99 	int unit;
100 
101 	unit = PCMUNIT(dev);
102 	if (unit >= devclass_get_maxunit(pcm_devclass))
103 		return;
104 	bdev = devclass_get_device(pcm_devclass, unit);
105 
106 	pcm_setflags(bdev, flags);
107 }
108 
109 /*
110  * return the channels associated with an open device instance.
111  * set the priority if the device is simplex and one direction (only) is
112  * specified.
113  * lock channels specified.
114  */
115 static int
116 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
117 {
118 	struct snddev_info *d;
119 	u_int32_t flags;
120 
121 	flags = dsp_get_flags(dev);
122 	d = dsp_get_info(dev);
123 	pcm_inprog(d, 1);
124 	pcm_lock(d);
125 	KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
126 		("getchns: read and write both prioritised"));
127 
128 	if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
129 		flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
130 		dsp_set_flags(dev, flags);
131 	}
132 
133 	*rdch = dev->si_drv1;
134 	*wrch = dev->si_drv2;
135 	if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
136 		if (prio) {
137 			if (*rdch && flags & SD_F_PRIO_WR) {
138 				dev->si_drv1 = NULL;
139 				*rdch = pcm_getfakechan(d);
140 			} else if (*wrch && flags & SD_F_PRIO_RD) {
141 				dev->si_drv2 = NULL;
142 				*wrch = pcm_getfakechan(d);
143 			}
144 		}
145 
146 		pcm_getfakechan(d)->flags |= CHN_F_BUSY;
147 	}
148 	pcm_unlock(d);
149 
150 	if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
151 		CHN_LOCK(*rdch);
152 	if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
153 		CHN_LOCK(*wrch);
154 
155 	return 0;
156 }
157 
158 /* unlock specified channels */
159 static void
160 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
161 {
162 	struct snddev_info *d;
163 
164 	d = dsp_get_info(dev);
165 	if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
166 		CHN_UNLOCK(wrch);
167 	if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
168 		CHN_UNLOCK(rdch);
169 	pcm_inprog(d, -1);
170 }
171 
172 static int
173 dsp_open(struct dev_open_args *ap)
174 {
175 	struct cdev *i_dev = ap->a_head.a_dev;
176 	struct thread *td = curthread;
177 	int flags = ap->a_oflags;
178 	struct pcm_channel *rdch, *wrch;
179 	struct snddev_info *d = NULL;
180 	struct snddev_channel *sce = NULL;
181 	u_int32_t fmt = AFMT_U8;
182 	int error;
183 	int chnum;
184 
185 	if (i_dev == NULL) {
186 		error = ENODEV;
187 		goto out;
188 	}
189 
190 	d = dsp_get_info(i_dev);
191 	SLIST_FOREACH(sce, &d->channels, link) {
192 		if (sce->dsp_dev == i_dev)
193 			break;
194 	}
195 
196 	if (sce == NULL) {
197 		error = ENODEV;
198 		goto out;
199 	}
200 
201 	if (td == NULL) {
202 		error = ENODEV;
203 		goto out;
204 	}
205 
206 	if ((flags & (FREAD | FWRITE)) == 0) {
207 		error = EINVAL;
208 		goto out;
209 	}
210 
211 	chnum = PCMCHAN(i_dev);
212 
213 	/* lock snddev so nobody else can monkey with it */
214 	pcm_lock(d);
215 
216 	rdch = i_dev->si_drv1;
217 	wrch = i_dev->si_drv2;
218 
219 	if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) &&
220 		    (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) {
221 		/* simplex or not, better safe than sorry. */
222 		pcm_unlock(d);
223 		error = EBUSY;
224 		goto out;
225 	}
226 
227 	/*
228 	 * if we get here, the open request is valid- either:
229 	 *   * we were previously not open
230 	 *   * we were open for play xor record and the opener wants
231 	 *     the non-open direction
232 	 */
233 	if (flags & FREAD) {
234 		/* open for read */
235 		pcm_unlock(d);
236 		error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum);
237 		if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE))
238 			error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1);
239 
240 		if (error == 0 && (chn_reset(rdch, fmt) ||
241 				(fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))))
242 			error = ENODEV;
243 
244 		if (error != 0) {
245 			if (rdch)
246 				pcm_chnrelease(rdch);
247 			goto out;
248 		}
249 
250 		pcm_chnref(rdch, 1);
251 	 	CHN_UNLOCK(rdch);
252 		pcm_lock(d);
253 	}
254 
255 	if (flags & FWRITE) {
256 	    /* open for write */
257 	    pcm_unlock(d);
258 	    error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum);
259 	    if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD))
260 	    	error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1);
261 
262 	    if (error == 0 && (chn_reset(wrch, fmt) ||
263 	    		(fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED))))
264 		error = ENODEV;
265 
266 	    if (error != 0) {
267 		if (wrch)
268 		    pcm_chnrelease(wrch);
269 		if (rdch) {
270 		    /*
271 		     * Lock, deref and release previously created record channel
272 		     */
273 		    CHN_LOCK(rdch);
274 		    pcm_chnref(rdch, -1);
275 		    pcm_chnrelease(rdch);
276 		}
277 
278 		goto out;
279 	    }
280 
281 	    pcm_chnref(wrch, 1);
282 	    CHN_UNLOCK(wrch);
283 	    pcm_lock(d);
284 	}
285 
286 	i_dev->si_drv1 = rdch;
287 	i_dev->si_drv2 = wrch;
288 
289 	sce->open++;
290 
291 	pcm_unlock(d);
292 	return 0;
293 
294 out:
295 	if (i_dev != NULL && sce != NULL && sce->open == 0) {
296 		pcm_lock(d);
297 		destroy_dev(i_dev);
298 		sce->dsp_dev = NULL;
299 		pcm_unlock(d);
300 	}
301 	return (error);
302 }
303 
304 static int
305 dsp_close(struct dev_close_args *ap)
306 {
307 	struct cdev *i_dev = ap->a_head.a_dev;
308 	struct pcm_channel *rdch, *wrch;
309 	struct snddev_info *d;
310 	struct snddev_channel *sce = NULL;
311 	int refs;
312 
313 	d = dsp_get_info(i_dev);
314 	pcm_lock(d);
315 	rdch = i_dev->si_drv1;
316 	wrch = i_dev->si_drv2;
317 	i_dev->si_drv1 = NULL;
318 	i_dev->si_drv2 = NULL;
319 
320 	SLIST_FOREACH(sce, &d->channels, link) {
321 		if (sce->dsp_dev == i_dev)
322 			break;
323 	}
324 	sce->dsp_dev = NULL;
325 	destroy_dev(i_dev);
326 
327 	pcm_unlock(d);
328 
329 	if (rdch || wrch) {
330 		refs = 0;
331 		if (rdch) {
332 			CHN_LOCK(rdch);
333 			refs += pcm_chnref(rdch, -1);
334 			chn_abort(rdch); /* won't sleep */
335 			rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
336 			chn_reset(rdch, 0);
337 			pcm_chnrelease(rdch);
338 		}
339 		if (wrch) {
340 			CHN_LOCK(wrch);
341 			refs += pcm_chnref(wrch, -1);
342 			/*
343 			 * XXX: Maybe the right behaviour is to abort on non_block.
344 			 * It seems that mplayer flushes the audio queue by quickly
345 			 * closing and re-opening.  In FBSD, there's a long pause
346 			 * while the audio queue flushes that I presume isn't there in
347 			 * linux.
348 			 */
349 			chn_flush(wrch); /* may sleep */
350 			wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
351 			chn_reset(wrch, 0);
352 			pcm_chnrelease(wrch);
353 		}
354 
355 		pcm_lock(d);
356 		/*
357 		 * If there are no more references, release the channels.
358 		 */
359 		if (refs == 0) {
360 			if (pcm_getfakechan(d))
361 				pcm_getfakechan(d)->flags = 0;
362 			/* What is this?!? */
363 			dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
364 		}
365 		pcm_unlock(d);
366 	}
367 	return 0;
368 }
369 
370 static int
371 dsp_read(struct dev_read_args *ap)
372 {
373 	struct cdev *i_dev = ap->a_head.a_dev;
374 	struct uio *buf = ap->a_uio;
375 	int flag = ap->a_ioflag;
376 	struct pcm_channel *rdch, *wrch;
377 	int ret;
378 
379 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
380 
381 	KASSERT(rdch, ("dsp_read: nonexistant channel"));
382 	KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
383 
384 	if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
385 		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
386 		return EINVAL;
387 	}
388 	if (!(rdch->flags & CHN_F_RUNNING))
389 		rdch->flags |= CHN_F_RUNNING;
390 	ret = chn_read(rdch, buf, flag);
391 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
392 
393 	return ret;
394 }
395 
396 static int
397 dsp_write(struct dev_write_args *ap)
398 {
399 	struct cdev *i_dev = ap->a_head.a_dev;
400 	struct uio *buf = ap->a_uio;
401 	int flag = ap->a_ioflag;
402 	struct pcm_channel *rdch, *wrch;
403 	int ret;
404 
405 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
406 
407 	KASSERT(wrch, ("dsp_write: nonexistant channel"));
408 	KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
409 
410 	if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
411 		relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
412 		return EINVAL;
413 	}
414 	if (!(wrch->flags & CHN_F_RUNNING))
415 		wrch->flags |= CHN_F_RUNNING;
416 	ret = chn_write(wrch, buf, flag);
417 	relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
418 
419 	return ret;
420 }
421 
422 static int
423 dsp_ioctl(struct dev_ioctl_args *ap)
424 {
425 	struct cdev *i_dev = ap->a_head.a_dev;
426 	u_long cmd = ap->a_cmd;
427 	caddr_t arg = ap->a_data;
428     	struct pcm_channel *chn, *rdch, *wrch;
429 	struct snddev_info *d;
430 	int kill;
431     	int ret = 0, *arg_i = (int *)arg, tmp;
432 
433 	d = dsp_get_info(i_dev);
434 	getchns(i_dev, &rdch, &wrch, 0);
435 
436 	kill = 0;
437 	if (wrch && (wrch->flags & CHN_F_DEAD))
438 		kill |= 1;
439 	if (rdch && (rdch->flags & CHN_F_DEAD))
440 		kill |= 2;
441 	if (kill == 3) {
442 		relchns(i_dev, rdch, wrch, 0);
443 		return EINVAL;
444 	}
445 	if (kill & 1)
446 		wrch = NULL;
447 	if (kill & 2)
448 		rdch = NULL;
449 
450 	/*
451 	 * 4Front OSS specifies that dsp devices allow mixer controls to
452 	 * control PCM == their volume.
453 	 */
454 	if (IOCGROUP(cmd) == 'M') {
455 		/*
456 		 * For now only set the channel volume for vchans, pass
457 		 * all others to the mixer.
458 		 */
459 		if (wrch != NULL && wrch->flags & CHN_F_VIRTUAL &&
460 		    (cmd & 0xff) == SOUND_MIXER_PCM) {
461 			if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
462 				int vol_raw = *(int *)arg;
463 				int vol_left, vol_right;
464 
465 				vol_left = min(vol_raw & 0x00ff, 100);
466 				vol_right = min((vol_raw & 0xff00) >> 8, 100);
467 				ret = chn_setvolume(wrch, vol_left, vol_right);
468 			} else {
469 				*(int *)arg = wrch->volume;
470 			}
471 		} else {
472 			ap->a_head.a_dev = d->mixer_dev;
473 			ret = mixer_ioctl(ap);
474 		}
475 
476 		relchns(i_dev, rdch, wrch, 0);
477 		return ret;
478 	}
479 
480     	switch(cmd) {
481 #ifdef OLDPCM_IOCTL
482     	/*
483      	 * we start with the new ioctl interface.
484      	 */
485     	case AIONWRITE:	/* how many bytes can write ? */
486 		if (wrch) {
487 			CHN_LOCK(wrch);
488 /*
489 		if (wrch && wrch->bufhard.dl)
490 			while (chn_wrfeed(wrch) == 0);
491 */
492 			*arg_i = sndbuf_getfree(wrch->bufsoft);
493 			CHN_UNLOCK(wrch);
494 		} else {
495 			*arg_i = 0;
496 			ret = EINVAL;
497 		}
498 		break;
499 
500     	case AIOSSIZE:     /* set the current blocksize */
501 		{
502 	    		struct snd_size *p = (struct snd_size *)arg;
503 
504 			p->play_size = 0;
505 			p->rec_size = 0;
506 	    		if (wrch) {
507 				CHN_LOCK(wrch);
508 				chn_setblocksize(wrch, 2, p->play_size);
509 				p->play_size = sndbuf_getblksz(wrch->bufsoft);
510 				CHN_UNLOCK(wrch);
511 			}
512 	    		if (rdch) {
513 				CHN_LOCK(rdch);
514 				chn_setblocksize(rdch, 2, p->rec_size);
515 				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
516 				CHN_UNLOCK(rdch);
517 			}
518 		}
519 		break;
520     	case AIOGSIZE:	/* get the current blocksize */
521 		{
522 	    		struct snd_size *p = (struct snd_size *)arg;
523 
524 	    		if (wrch) {
525 				CHN_LOCK(wrch);
526 				p->play_size = sndbuf_getblksz(wrch->bufsoft);
527 				CHN_UNLOCK(wrch);
528 			}
529 	    		if (rdch) {
530 				CHN_LOCK(rdch);
531 				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
532 				CHN_UNLOCK(rdch);
533 			}
534 		}
535 		break;
536 
537     	case AIOSFMT:
538     	case AIOGFMT:
539 		{
540 	    		snd_chan_param *p = (snd_chan_param *)arg;
541 
542 			if (cmd == AIOSFMT &&
543 			    ((p->play_format != 0 && p->play_rate == 0) ||
544 			    (p->rec_format != 0 && p->rec_rate == 0))) {
545 				ret = EINVAL;
546 				break;
547 			}
548 	    		if (wrch) {
549 				CHN_LOCK(wrch);
550 				if (cmd == AIOSFMT && p->play_format != 0) {
551 					chn_setformat(wrch, p->play_format);
552 					chn_setspeed(wrch, p->play_rate);
553 				}
554 	    			p->play_rate = wrch->speed;
555 	    			p->play_format = wrch->format;
556 				CHN_UNLOCK(wrch);
557 			} else {
558 	    			p->play_rate = 0;
559 	    			p->play_format = 0;
560 	    		}
561 	    		if (rdch) {
562 				CHN_LOCK(rdch);
563 				if (cmd == AIOSFMT && p->rec_format != 0) {
564 					chn_setformat(rdch, p->rec_format);
565 					chn_setspeed(rdch, p->rec_rate);
566 				}
567 				p->rec_rate = rdch->speed;
568 				p->rec_format = rdch->format;
569 				CHN_UNLOCK(rdch);
570 			} else {
571 	    			p->rec_rate = 0;
572 	    			p->rec_format = 0;
573 	    		}
574 		}
575 		break;
576 
577     	case AIOGCAP:     /* get capabilities */
578 		{
579 	    		snd_capabilities *p = (snd_capabilities *)arg;
580 			struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
581 			struct cdev *pdev;
582 
583 			if (rdch) {
584 				CHN_LOCK(rdch);
585 				rcaps = chn_getcaps(rdch);
586 			}
587 			if (wrch) {
588 				CHN_LOCK(wrch);
589 				pcaps = chn_getcaps(wrch);
590 			}
591 	    		p->rate_min = max(rcaps? rcaps->minspeed : 0,
592 	                      		  pcaps? pcaps->minspeed : 0);
593 	    		p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
594 	                      		  pcaps? pcaps->maxspeed : 1000000);
595 	    		p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
596 	                     		 wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
597 			/* XXX bad on sb16 */
598 	    		p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
599 			 	     (wrch? chn_getformats(wrch) : 0xffffffff);
600 			if (rdch && wrch)
601 				p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
602 			pdev = d->mixer_dev;
603 	    		p->mixers = 1; /* default: one mixer */
604 	    		p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
605 	    		p->left = p->right = 100;
606 			if (rdch)
607 				CHN_UNLOCK(rdch);
608 			if (wrch)
609 				CHN_UNLOCK(wrch);
610 		}
611 		break;
612 
613     	case AIOSTOP:
614 		if (*arg_i == AIOSYNC_PLAY && wrch) {
615 			CHN_LOCK(wrch);
616 			*arg_i = chn_abort(wrch);
617 			CHN_UNLOCK(wrch);
618 		} else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
619 			CHN_LOCK(rdch);
620 			*arg_i = chn_abort(rdch);
621 			CHN_UNLOCK(rdch);
622 		} else {
623 	   	 	kprintf("AIOSTOP: bad channel 0x%x\n", *arg_i);
624 	    		*arg_i = 0;
625 		}
626 		break;
627 
628     	case AIOSYNC:
629 		kprintf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
630 	    		((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
631 		break;
632 #endif
633 	/*
634 	 * here follow the standard ioctls (filio.h etc.)
635 	 */
636     	case FIONREAD: /* get # bytes to read */
637 		if (rdch) {
638 			CHN_LOCK(rdch);
639 /*			if (rdch && rdch->bufhard.dl)
640 				while (chn_rdfeed(rdch) == 0);
641 */
642 			*arg_i = sndbuf_getready(rdch->bufsoft);
643 			CHN_UNLOCK(rdch);
644 		} else {
645 			*arg_i = 0;
646 			ret = EINVAL;
647 		}
648 		break;
649 
650     	case FIOASYNC: /*set/clear async i/o */
651 		DEB( kprintf("FIOASYNC\n") ; )
652 		break;
653 
654     	case SNDCTL_DSP_NONBLOCK:
655     	case FIONBIO: /* set/clear non-blocking i/o */
656 		if (rdch) {
657 			CHN_LOCK(rdch);
658 			if (*arg_i)
659 				rdch->flags |= CHN_F_NBIO;
660 			else
661 				rdch->flags &= ~CHN_F_NBIO;
662 			CHN_UNLOCK(rdch);
663 		}
664 		if (wrch) {
665 			CHN_LOCK(wrch);
666 			if (*arg_i)
667 				wrch->flags |= CHN_F_NBIO;
668 			else
669 				wrch->flags &= ~CHN_F_NBIO;
670 			CHN_UNLOCK(wrch);
671 		}
672 		break;
673 
674     	/*
675 	 * Finally, here is the linux-compatible ioctl interface
676 	 */
677 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
678     	case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
679     	case SNDCTL_DSP_GETBLKSIZE:
680 		chn = wrch ? wrch : rdch;
681 		if (chn) {
682 			CHN_LOCK(chn);
683 			*arg_i = sndbuf_getblksz(chn->bufsoft);
684 			CHN_UNLOCK(chn);
685 		} else {
686 			*arg_i = 0;
687 			ret = EINVAL;
688 		}
689 		break ;
690 
691     	case SNDCTL_DSP_SETBLKSIZE:
692 		RANGE(*arg_i, 16, 65536);
693 		if (wrch) {
694 			CHN_LOCK(wrch);
695 			chn_setblocksize(wrch, 2, *arg_i);
696 			CHN_UNLOCK(wrch);
697 		}
698 		if (rdch) {
699 			CHN_LOCK(rdch);
700 			chn_setblocksize(rdch, 2, *arg_i);
701 			CHN_UNLOCK(rdch);
702 		}
703 		break;
704 
705     	case SNDCTL_DSP_RESET:
706 		DEB(kprintf("dsp reset\n"));
707 		if (wrch) {
708 			CHN_LOCK(wrch);
709 			chn_abort(wrch);
710 			chn_resetbuf(wrch);
711 			CHN_UNLOCK(wrch);
712 		}
713 		if (rdch) {
714 			CHN_LOCK(rdch);
715 			chn_abort(rdch);
716 			chn_resetbuf(rdch);
717 			CHN_UNLOCK(rdch);
718 		}
719 		break;
720 
721     	case SNDCTL_DSP_SYNC:
722 		DEB(kprintf("dsp sync\n"));
723 		/* chn_sync may sleep */
724 		if (wrch) {
725 			CHN_LOCK(wrch);
726 			chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
727 			CHN_UNLOCK(wrch);
728 		}
729 		break;
730 
731     	case SNDCTL_DSP_SPEED:
732 		/* chn_setspeed may sleep */
733 		tmp = 0;
734 		if (wrch) {
735 			CHN_LOCK(wrch);
736 			ret = chn_setspeed(wrch, *arg_i);
737 			tmp = wrch->speed;
738 			CHN_UNLOCK(wrch);
739 		}
740 		if (rdch && ret == 0) {
741 			CHN_LOCK(rdch);
742 			ret = chn_setspeed(rdch, *arg_i);
743 			if (tmp == 0)
744 				tmp = rdch->speed;
745 			CHN_UNLOCK(rdch);
746 		}
747 		*arg_i = tmp;
748 		break;
749 
750     	case SOUND_PCM_READ_RATE:
751 		chn = wrch ? wrch : rdch;
752 		if (chn) {
753 			CHN_LOCK(chn);
754 			*arg_i = chn->speed;
755 			CHN_UNLOCK(chn);
756 		} else {
757 			*arg_i = 0;
758 			ret = EINVAL;
759 		}
760 		break;
761 
762     	case SNDCTL_DSP_STEREO:
763 		tmp = -1;
764 		*arg_i = (*arg_i)? AFMT_STEREO : 0;
765 		if (wrch) {
766 			CHN_LOCK(wrch);
767 			ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
768 			tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
769 			CHN_UNLOCK(wrch);
770 		}
771 		if (rdch && ret == 0) {
772 			CHN_LOCK(rdch);
773 			ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
774 			if (tmp == -1)
775 				tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
776 			CHN_UNLOCK(rdch);
777 		}
778 		*arg_i = tmp;
779 		break;
780 
781     	case SOUND_PCM_WRITE_CHANNELS:
782 /*	case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
783 		if (*arg_i != 0) {
784 			tmp = 0;
785 			*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
786 	  		if (wrch) {
787 				CHN_LOCK(wrch);
788 				ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
789 				tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
790 				CHN_UNLOCK(wrch);
791 			}
792 			if (rdch && ret == 0) {
793 				CHN_LOCK(rdch);
794 				ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
795 				if (tmp == 0)
796 					tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
797 				CHN_UNLOCK(rdch);
798 			}
799 			*arg_i = tmp;
800 		} else {
801 			chn = wrch ? wrch : rdch;
802 			CHN_LOCK(chn);
803 			*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
804 			CHN_UNLOCK(chn);
805 		}
806 		break;
807 
808     	case SOUND_PCM_READ_CHANNELS:
809 		chn = wrch ? wrch : rdch;
810 		if (chn) {
811 			CHN_LOCK(chn);
812 			*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
813 			CHN_UNLOCK(chn);
814 		} else {
815 			*arg_i = 0;
816 			ret = EINVAL;
817 		}
818 		break;
819 
820     	case SNDCTL_DSP_GETFMTS:	/* returns a mask of supported fmts */
821 		chn = wrch ? wrch : rdch;
822 		if (chn) {
823 			CHN_LOCK(chn);
824 			*arg_i = chn_getformats(chn);
825 			CHN_UNLOCK(chn);
826 		} else {
827 			*arg_i = 0;
828 			ret = EINVAL;
829 		}
830 		break ;
831 
832     	case SNDCTL_DSP_SETFMT:	/* sets _one_ format */
833 		if ((*arg_i != AFMT_QUERY)) {
834 			tmp = 0;
835 			if (wrch) {
836 				CHN_LOCK(wrch);
837 				ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
838 				tmp = wrch->format & ~AFMT_STEREO;
839 				CHN_UNLOCK(wrch);
840 			}
841 			if (rdch && ret == 0) {
842 				CHN_LOCK(rdch);
843 				ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
844 				if (tmp == 0)
845 					tmp = rdch->format & ~AFMT_STEREO;
846 				CHN_UNLOCK(rdch);
847 			}
848 			*arg_i = tmp;
849 		} else {
850 			chn = wrch ? wrch : rdch;
851 			CHN_LOCK(chn);
852 			*arg_i = chn->format & ~AFMT_STEREO;
853 			CHN_UNLOCK(chn);
854 		}
855 		break;
856 
857     	case SNDCTL_DSP_SETFRAGMENT:
858 		DEB(kprintf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
859 		{
860 			u_int32_t fragln = (*arg_i) & 0x0000ffff;
861 			u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
862 			u_int32_t fragsz;
863 			u_int32_t r_maxfrags, r_fragsz;
864 
865 			RANGE(fragln, 4, 16);
866 			fragsz = 1 << fragln;
867 
868 			if (maxfrags == 0)
869 				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
870 			if (maxfrags < 2)
871 				maxfrags = 2;
872 			if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
873 				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
874 
875 			DEB(kprintf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
876 		    	if (rdch) {
877 				CHN_LOCK(rdch);
878 				ret = chn_setblocksize(rdch, maxfrags, fragsz);
879 				r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
880 				r_fragsz = sndbuf_getblksz(rdch->bufsoft);
881 				CHN_UNLOCK(rdch);
882 			} else {
883 				r_maxfrags = maxfrags;
884 				r_fragsz = fragsz;
885 			}
886 		    	if (wrch && ret == 0) {
887 				CHN_LOCK(wrch);
888 				ret = chn_setblocksize(wrch, maxfrags, fragsz);
889  				maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
890 				fragsz = sndbuf_getblksz(wrch->bufsoft);
891 				CHN_UNLOCK(wrch);
892 			} else { /* use whatever came from the read channel */
893 				maxfrags = r_maxfrags;
894 				fragsz = r_fragsz;
895 			}
896 
897 			fragln = 0;
898 			while (fragsz > 1) {
899 				fragln++;
900 				fragsz >>= 1;
901 			}
902 	    		*arg_i = (maxfrags << 16) | fragln;
903 		}
904 		break;
905 
906     	case SNDCTL_DSP_GETISPACE:
907 		/* return the size of data available in the input queue */
908 		{
909 	    		audio_buf_info *a = (audio_buf_info *)arg;
910 	    		if (rdch) {
911 	        		struct snd_dbuf *bs = rdch->bufsoft;
912 
913 				CHN_LOCK(rdch);
914 				a->bytes = sndbuf_getready(bs);
915 	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
916 	        		a->fragstotal = sndbuf_getblkcnt(bs);
917 	        		a->fragsize = sndbuf_getblksz(bs);
918 				CHN_UNLOCK(rdch);
919 	    		}
920 		}
921 		break;
922 
923     	case SNDCTL_DSP_GETOSPACE:
924 		/* return space available in the output queue */
925 		{
926 	    		audio_buf_info *a = (audio_buf_info *)arg;
927 	    		if (wrch) {
928 	        		struct snd_dbuf *bs = wrch->bufsoft;
929 
930 				CHN_LOCK(wrch);
931 				/* XXX abusive DMA update: chn_wrupdate(wrch); */
932 				a->bytes = sndbuf_getfree(bs);
933 	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
934 	        		a->fragstotal = sndbuf_getblkcnt(bs);
935 	        		a->fragsize = sndbuf_getblksz(bs);
936 				CHN_UNLOCK(wrch);
937 	    		}
938 		}
939 		break;
940 
941     	case SNDCTL_DSP_GETIPTR:
942 		{
943 	    		count_info *a = (count_info *)arg;
944 	    		if (rdch) {
945 	        		struct snd_dbuf *bs = rdch->bufsoft;
946 
947 				CHN_LOCK(rdch);
948 				/* XXX abusive DMA update: chn_rdupdate(rdch); */
949 	        		a->bytes = sndbuf_gettotal(bs);
950 	        		a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
951 	        		a->ptr = sndbuf_getreadyptr(bs);
952 				rdch->blocks = sndbuf_getblocks(bs);
953 				CHN_UNLOCK(rdch);
954 	    		} else
955 				ret = EINVAL;
956 		}
957 		break;
958 
959     	case SNDCTL_DSP_GETOPTR:
960 		{
961 	    		count_info *a = (count_info *)arg;
962 	    		if (wrch) {
963 	        		struct snd_dbuf *bs = wrch->bufsoft;
964 
965 				CHN_LOCK(wrch);
966 				/* XXX abusive DMA update: chn_wrupdate(wrch); */
967 	        		a->bytes = sndbuf_gettotal(bs);
968 	        		a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
969 	        		a->ptr = sndbuf_getreadyptr(bs);
970 				wrch->blocks = sndbuf_getblocks(bs);
971 				CHN_UNLOCK(wrch);
972 	    		} else
973 				ret = EINVAL;
974 		}
975 		break;
976 
977     	case SNDCTL_DSP_GETCAPS:
978 		*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
979 		if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
980 			*arg_i |= DSP_CAP_DUPLEX;
981 		break;
982 
983     	case SOUND_PCM_READ_BITS:
984 		chn = wrch ? wrch : rdch;
985 		if (chn) {
986 			CHN_LOCK(chn);
987 			if (chn->format & AFMT_8BIT)
988 				*arg_i = 8;
989 			else if (chn->format & AFMT_16BIT)
990 				*arg_i = 16;
991 			else if (chn->format & AFMT_24BIT)
992 				*arg_i = 24;
993 			else if (chn->format & AFMT_32BIT)
994 				*arg_i = 32;
995 			else
996 				ret = EINVAL;
997 			CHN_UNLOCK(chn);
998 		} else {
999 			*arg_i = 0;
1000 			ret = EINVAL;
1001 		}
1002 		break;
1003 
1004     	case SNDCTL_DSP_SETTRIGGER:
1005 		if (rdch) {
1006 			CHN_LOCK(rdch);
1007 			rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1008 		    	if (*arg_i & PCM_ENABLE_INPUT)
1009 				chn_start(rdch, 1);
1010 			else
1011 				rdch->flags |= CHN_F_NOTRIGGER;
1012 			CHN_UNLOCK(rdch);
1013 		}
1014 		if (wrch) {
1015 			CHN_LOCK(wrch);
1016 			wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
1017 		    	if (*arg_i & PCM_ENABLE_OUTPUT)
1018 				chn_start(wrch, 1);
1019 			else
1020 				wrch->flags |= CHN_F_NOTRIGGER;
1021 			CHN_UNLOCK(wrch);
1022 		}
1023 		break;
1024 
1025     	case SNDCTL_DSP_GETTRIGGER:
1026 		*arg_i = 0;
1027 		if (wrch) {
1028 			CHN_LOCK(wrch);
1029 			if (wrch->flags & CHN_F_TRIGGERED)
1030 				*arg_i |= PCM_ENABLE_OUTPUT;
1031 			CHN_UNLOCK(wrch);
1032 		}
1033 		if (rdch) {
1034 			CHN_LOCK(rdch);
1035 			if (rdch->flags & CHN_F_TRIGGERED)
1036 				*arg_i |= PCM_ENABLE_INPUT;
1037 			CHN_UNLOCK(rdch);
1038 		}
1039 		break;
1040 
1041 	case SNDCTL_DSP_GETODELAY:
1042 		if (wrch) {
1043 			struct snd_dbuf *b = wrch->bufhard;
1044 	        	struct snd_dbuf *bs = wrch->bufsoft;
1045 
1046 			CHN_LOCK(wrch);
1047 			/* XXX abusive DMA update: chn_wrupdate(wrch); */
1048 			*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
1049 			CHN_UNLOCK(wrch);
1050 		} else
1051 			ret = EINVAL;
1052 		break;
1053 
1054     	case SNDCTL_DSP_POST:
1055 		if (wrch) {
1056 			CHN_LOCK(wrch);
1057 			wrch->flags &= ~CHN_F_NOTRIGGER;
1058 			chn_start(wrch, 1);
1059 			CHN_UNLOCK(wrch);
1060 		}
1061 		break;
1062 
1063 	case SNDCTL_DSP_SETDUPLEX:
1064 		/*
1065 		 * switch to full-duplex mode if card is in half-duplex
1066 		 * mode and is able to work in full-duplex mode
1067 		 */
1068 		if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
1069 			dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
1070 		break;
1071 
1072     	case SNDCTL_DSP_MAPINBUF:
1073     	case SNDCTL_DSP_MAPOUTBUF:
1074     	case SNDCTL_DSP_SETSYNCRO:
1075 		/* undocumented */
1076 
1077     	case SNDCTL_DSP_SUBDIVIDE:
1078     	case SOUND_PCM_WRITE_FILTER:
1079     	case SOUND_PCM_READ_FILTER:
1080 		/* dunno what these do, don't sound important */
1081 
1082     	default:
1083 		DEB(kprintf("default ioctl fn 0x%08lx fail\n", cmd));
1084 		ret = EINVAL;
1085 		break;
1086     	}
1087 	relchns(i_dev, rdch, wrch, 0);
1088     	return ret;
1089 }
1090 
1091 static int
1092 dsp_poll(struct dev_poll_args *ap)
1093 {
1094 	struct cdev *i_dev = ap->a_head.a_dev;
1095 	int events = ap->a_events;
1096 	struct thread *td = curthread;
1097 	struct pcm_channel *wrch = NULL, *rdch = NULL;
1098 	int ret, e;
1099 
1100 	ret = 0;
1101 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1102 
1103 	if (wrch) {
1104 		e = (events & (POLLOUT | POLLWRNORM));
1105 		if (e)
1106 			ret |= chn_poll(wrch, e, td);
1107 	}
1108 	if (rdch) {
1109 		e = (events & (POLLIN | POLLRDNORM));
1110 		if (e)
1111 			ret |= chn_poll(rdch, e, td);
1112 	}
1113 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1114 
1115 	ap->a_events = ret;
1116 	return (0);
1117 }
1118 
1119 static struct filterops dsp_read_filtops =
1120 	{ 1, NULL, dsp_filter_detach, dsp_filter_read };
1121 static struct filterops dsp_write_filtops =
1122 	{ 1, NULL, dsp_filter_detach, dsp_filter_write };
1123 
1124 static int
1125 dsp_kqfilter(struct dev_kqfilter_args *ap)
1126 {
1127 	struct knote *kn = ap->a_kn;
1128 	struct klist *klist;
1129 	struct cdev *i_dev = ap->a_head.a_dev;
1130 	struct pcm_channel *wrch = NULL, *rdch = NULL;
1131 	struct snd_dbuf *bs;
1132 
1133 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1134 	ap->a_result = 1;
1135 	switch (kn->kn_filter) {
1136 	case EVFILT_READ:
1137 		if (rdch) {
1138 			kn->kn_fop = &dsp_read_filtops;
1139 			kn->kn_hook = (caddr_t)rdch;
1140 			bs = rdch->bufsoft;
1141 			ap->a_result = 0;
1142 		}
1143 		break;
1144 	case EVFILT_WRITE:
1145 		if (wrch) {
1146 			kn->kn_fop = &dsp_write_filtops;
1147 			kn->kn_hook = (caddr_t)wrch;
1148 			bs = wrch->bufsoft;
1149 			ap->a_result = 0;
1150 		}
1151 		break;
1152 	}
1153 
1154 	if (ap->a_result == 0) {
1155 		crit_enter();
1156 		klist = &sndbuf_getsel(bs)->si_note;
1157 		SLIST_INSERT_HEAD(klist, kn, kn_selnext);
1158 		crit_exit();
1159 	}
1160 
1161 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1162 
1163 	return (0);
1164 }
1165 
1166 static void
1167 dsp_filter_detach(struct knote *kn)
1168 {
1169 	struct pcm_channel *ch = (struct pcm_channel *)kn->kn_hook;
1170 	struct snd_dbuf *bs = ch->bufsoft;
1171 	struct klist *klist;
1172 
1173 	CHN_LOCK(ch);
1174 	crit_enter();
1175 	klist = &sndbuf_getsel(bs)->si_note;
1176 	SLIST_REMOVE(klist, kn, knote, kn_selnext);
1177 	crit_exit();
1178 	CHN_UNLOCK(ch);
1179 }
1180 
1181 static int
1182 dsp_filter_read(struct knote *kn, long hint)
1183 {
1184 	struct pcm_channel *rdch = (struct pcm_channel *)kn->kn_hook;
1185 	struct thread *td = curthread;
1186 	int ready;
1187 
1188 	CHN_LOCK(rdch);
1189 	ready = chn_poll(rdch, 1, td);
1190 	CHN_UNLOCK(rdch);
1191 
1192 	return (ready);
1193 }
1194 
1195 static int
1196 dsp_filter_write(struct knote *kn, long hint)
1197 {
1198 	struct pcm_channel *wrch = (struct pcm_channel *)kn->kn_hook;
1199 	struct thread *td = curthread;
1200 	int ready;
1201 
1202 	CHN_LOCK(wrch);
1203 	ready = chn_poll(wrch, 1, td);
1204 	CHN_UNLOCK(wrch);
1205 
1206 	return (ready);
1207 }
1208 
1209 static int
1210 dsp_mmap(struct dev_mmap_args *ap)
1211 {
1212 	struct cdev *i_dev = ap->a_head.a_dev;
1213 	vm_offset_t offset = ap->a_offset;
1214 	int nprot = ap->a_nprot;
1215 	struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1216 
1217 	if (nprot & PROT_EXEC)
1218 		return -1;
1219 
1220 	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1221 #if 0
1222 	/*
1223 	 * XXX the linux api uses the nprot to select read/write buffer
1224 	 * our vm system doesn't allow this, so force write buffer
1225 	 */
1226 
1227 	if (wrch && (nprot & PROT_WRITE)) {
1228 		c = wrch;
1229 	} else if (rdch && (nprot & PROT_READ)) {
1230 		c = rdch;
1231 	} else {
1232 		return -1;
1233 	}
1234 #else
1235 	c = wrch;
1236 #endif
1237 
1238 	if (c == NULL) {
1239 		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1240 		return -1;
1241 	}
1242 
1243 	if (offset >= sndbuf_getsize(c->bufsoft)) {
1244 		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1245 		return -1;
1246 	}
1247 
1248 	if (!(c->flags & CHN_F_MAPPED))
1249 		c->flags |= CHN_F_MAPPED;
1250 
1251 	ap->a_result = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset)));
1252 	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1253 
1254 	return (0);
1255 }
1256 
1257 /*
1258  *    for i = 0 to channels of device N
1259  *	if dspN.i isn't busy and in the right dir, create a dev_t and return it
1260  */
1261 int
1262 dsp_clone(struct dev_clone_args *ap)
1263 {
1264 	struct cdev *i_dev = ap->a_head.a_dev;
1265 	struct cdev *pdev;
1266 	struct snddev_info *pcm_dev;
1267 	struct snddev_channel *pcm_chan;
1268 	struct pcm_channel *c;
1269 	int err = EBUSY;
1270 	int dir;
1271 
1272 	pcm_dev = dsp_get_info(i_dev);
1273 
1274 	if (pcm_dev == NULL)
1275 		return (ENODEV);
1276 
1277 	dir = ap->a_mode & FWRITE ? PCMDIR_PLAY : PCMDIR_REC;
1278 
1279 retry_chnalloc:
1280 	SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
1281 		c = pcm_chan->channel;
1282 		CHN_LOCK(c);
1283 		pdev = pcm_chan->dsp_dev;
1284 
1285 		/*
1286 		 * Make sure that the channel has not been assigned
1287 		 * to a device yet (and vice versa).
1288 		 * The direction has to match and the channel may not
1289 		 * be busy.
1290 		 * dsp_open will use exactly this channel number to
1291 		 * avoid (possible?) races between clone and open.
1292 		 */
1293 		if (pdev == NULL && c->direction == dir &&
1294 		    !(c->flags & CHN_F_BUSY)) {
1295 			CHN_UNLOCK(c);
1296 			pcm_lock(pcm_dev);
1297 			pcm_chan->dsp_dev = make_only_dev(&dsp_cdevsw,
1298 				PCMMKMINOR(PCMUNIT(i_dev), pcm_chan->chan_num),
1299 				UID_ROOT, GID_WHEEL,
1300 				0666,
1301 				"%s.%d",
1302 				devtoname(i_dev),
1303 				pcm_chan->chan_num);
1304 			pcm_unlock(pcm_dev);
1305 
1306 			ap->a_dev = pcm_chan->dsp_dev;
1307 			return (0);
1308 		}
1309 		CHN_UNLOCK(c);
1310 
1311 #if DEBUG
1312 		if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1313 			kprintf("%s: dangling device\n", devtoname(pdev));
1314 		}
1315 #endif
1316 	}
1317 
1318 	/* no channel available, create vchannel */
1319 	if (dir == PCMDIR_PLAY &&
1320 	    pcm_dev->vchancount > 0 &&
1321 	    pcm_dev->vchancount < snd_maxautovchans &&
1322 	    pcm_dev->devcount < PCMMAXCHAN) {
1323 		err = pcm_setvchans(pcm_dev, pcm_dev->vchancount + 1);
1324 		if (err == 0)
1325 			goto retry_chnalloc;
1326 		/*
1327 		 * If we can't use vchans, because the main output is
1328 		 * blocked for something else, we should not return
1329 		 * any vchan create error, but the more descriptive
1330 		 * EBUSY.
1331 		 * After all, the user didn't ask us to clone, but
1332 		 * only opened /dev/dsp.
1333 		 */
1334 		err = EBUSY;
1335 	}
1336 
1337 	return (err);
1338 }
1339