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