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