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