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