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