dsp.c revision 130640
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 130640 2004-06-17 17:16:53Z 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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *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(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
446{
447    	struct pcm_channel *chn, *rdch, *wrch;
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    	switch(cmd) {
481#ifdef OLDPCM_IOCTL
482    	/*
483     	 * we start with the new ioctl interface.
484     	 */
485    	case AIONWRITE:	/* how many bytes can write ? */
486		CHN_LOCK(wrch);
487/*
488		if (wrch && wrch->bufhard.dl)
489			while (chn_wrfeed(wrch) == 0);
490*/
491		*arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
492		CHN_UNLOCK(wrch);
493		break;
494
495    	case AIOSSIZE:     /* set the current blocksize */
496		{
497	    		struct snd_size *p = (struct snd_size *)arg;
498
499			p->play_size = 0;
500			p->rec_size = 0;
501	    		if (wrch) {
502				CHN_LOCK(wrch);
503				chn_setblocksize(wrch, 2, p->play_size);
504				p->play_size = sndbuf_getblksz(wrch->bufsoft);
505				CHN_UNLOCK(wrch);
506			}
507	    		if (rdch) {
508				CHN_LOCK(rdch);
509				chn_setblocksize(rdch, 2, p->rec_size);
510				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
511				CHN_UNLOCK(rdch);
512			}
513		}
514		break;
515    	case AIOGSIZE:	/* get the current blocksize */
516		{
517	    		struct snd_size *p = (struct snd_size *)arg;
518
519	    		if (wrch) {
520				CHN_LOCK(wrch);
521				p->play_size = sndbuf_getblksz(wrch->bufsoft);
522				CHN_UNLOCK(wrch);
523			}
524	    		if (rdch) {
525				CHN_LOCK(rdch);
526				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
527				CHN_UNLOCK(rdch);
528			}
529		}
530		break;
531
532    	case AIOSFMT:
533    	case AIOGFMT:
534		{
535	    		snd_chan_param *p = (snd_chan_param *)arg;
536
537	    		if (wrch) {
538				CHN_LOCK(wrch);
539				if (cmd == AIOSFMT) {
540					chn_setformat(wrch, p->play_format);
541					chn_setspeed(wrch, p->play_rate);
542				}
543	    			p->play_rate = wrch->speed;
544	    			p->play_format = wrch->format;
545				CHN_UNLOCK(wrch);
546			} else {
547	    			p->play_rate = 0;
548	    			p->play_format = 0;
549	    		}
550	    		if (rdch) {
551				CHN_LOCK(rdch);
552				if (cmd == AIOSFMT) {
553					chn_setformat(rdch, p->rec_format);
554					chn_setspeed(rdch, p->rec_rate);
555				}
556				p->rec_rate = rdch->speed;
557				p->rec_format = rdch->format;
558				CHN_UNLOCK(rdch);
559			} else {
560	    			p->rec_rate = 0;
561	    			p->rec_format = 0;
562	    		}
563		}
564		break;
565
566    	case AIOGCAP:     /* get capabilities */
567		{
568	    		snd_capabilities *p = (snd_capabilities *)arg;
569			struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
570			struct cdev *pdev;
571
572			if (rdch) {
573				CHN_LOCK(rdch);
574				rcaps = chn_getcaps(rdch);
575			}
576			if (wrch) {
577				CHN_LOCK(wrch);
578				pcaps = chn_getcaps(wrch);
579			}
580	    		p->rate_min = max(rcaps? rcaps->minspeed : 0,
581	                      		  pcaps? pcaps->minspeed : 0);
582	    		p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
583	                      		  pcaps? pcaps->maxspeed : 1000000);
584	    		p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
585	                     		 wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
586			/* XXX bad on sb16 */
587	    		p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
588			 	     (wrch? chn_getformats(wrch) : 0xffffffff);
589			if (rdch && wrch)
590				p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
591			pdev = d->mixer_dev;
592	    		p->mixers = 1; /* default: one mixer */
593	    		p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
594	    		p->left = p->right = 100;
595			if (rdch)
596				CHN_UNLOCK(rdch);
597			if (wrch)
598				CHN_UNLOCK(wrch);
599		}
600		break;
601
602    	case AIOSTOP:
603		if (*arg_i == AIOSYNC_PLAY && wrch) {
604			CHN_LOCK(wrch);
605			*arg_i = chn_abort(wrch);
606			CHN_UNLOCK(wrch);
607		} else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
608			CHN_LOCK(rdch);
609			*arg_i = chn_abort(rdch);
610			CHN_UNLOCK(rdch);
611		} else {
612	   	 	printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
613	    		*arg_i = 0;
614		}
615		break;
616
617    	case AIOSYNC:
618		printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
619	    		((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
620		break;
621#endif
622	/*
623	 * here follow the standard ioctls (filio.h etc.)
624	 */
625    	case FIONREAD: /* get # bytes to read */
626		if (rdch) {
627			CHN_LOCK(rdch);
628/*			if (rdch && rdch->bufhard.dl)
629				while (chn_rdfeed(rdch) == 0);
630*/
631			*arg_i = sndbuf_getready(rdch->bufsoft);
632			CHN_UNLOCK(rdch);
633		} else
634			*arg_i = 0;
635		break;
636
637    	case FIOASYNC: /*set/clear async i/o */
638		DEB( printf("FIOASYNC\n") ; )
639		break;
640
641    	case SNDCTL_DSP_NONBLOCK:
642    	case FIONBIO: /* set/clear non-blocking i/o */
643		if (rdch) {
644			CHN_LOCK(rdch);
645			if (*arg_i)
646				rdch->flags |= CHN_F_NBIO;
647			else
648				rdch->flags &= ~CHN_F_NBIO;
649			CHN_UNLOCK(rdch);
650		}
651		if (wrch) {
652			CHN_LOCK(wrch);
653			if (*arg_i)
654				wrch->flags |= CHN_F_NBIO;
655			else
656				wrch->flags &= ~CHN_F_NBIO;
657			CHN_UNLOCK(wrch);
658		}
659		break;
660
661    	/*
662	 * Finally, here is the linux-compatible ioctl interface
663	 */
664#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
665    	case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
666    	case SNDCTL_DSP_GETBLKSIZE:
667		chn = wrch ? wrch : rdch;
668		CHN_LOCK(chn);
669		*arg_i = sndbuf_getblksz(chn->bufsoft);
670		CHN_UNLOCK(chn);
671		break ;
672
673    	case SNDCTL_DSP_SETBLKSIZE:
674		RANGE(*arg_i, 16, 65536);
675		if (wrch) {
676			CHN_LOCK(wrch);
677			chn_setblocksize(wrch, 2, *arg_i);
678			CHN_UNLOCK(wrch);
679		}
680		if (rdch) {
681			CHN_LOCK(rdch);
682			chn_setblocksize(rdch, 2, *arg_i);
683			CHN_UNLOCK(rdch);
684		}
685		break;
686
687    	case SNDCTL_DSP_RESET:
688		DEB(printf("dsp reset\n"));
689		if (wrch) {
690			CHN_LOCK(wrch);
691			chn_abort(wrch);
692			chn_resetbuf(wrch);
693			CHN_UNLOCK(wrch);
694		}
695		if (rdch) {
696			CHN_LOCK(rdch);
697			chn_abort(rdch);
698			chn_resetbuf(rdch);
699			CHN_UNLOCK(rdch);
700		}
701		break;
702
703    	case SNDCTL_DSP_SYNC:
704		DEB(printf("dsp sync\n"));
705		/* chn_sync may sleep */
706		if (wrch) {
707			CHN_LOCK(wrch);
708			chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
709			CHN_UNLOCK(wrch);
710		}
711		break;
712
713    	case SNDCTL_DSP_SPEED:
714		/* chn_setspeed may sleep */
715		tmp = 0;
716		if (wrch) {
717			CHN_LOCK(wrch);
718			ret = chn_setspeed(wrch, *arg_i);
719			tmp = wrch->speed;
720			CHN_UNLOCK(wrch);
721		}
722		if (rdch && ret == 0) {
723			CHN_LOCK(rdch);
724			ret = chn_setspeed(rdch, *arg_i);
725			if (tmp == 0)
726				tmp = rdch->speed;
727			CHN_UNLOCK(rdch);
728		}
729		*arg_i = tmp;
730		break;
731
732    	case SOUND_PCM_READ_RATE:
733		chn = wrch ? wrch : rdch;
734		CHN_LOCK(chn);
735		*arg_i = chn->speed;
736		CHN_UNLOCK(chn);
737		break;
738
739    	case SNDCTL_DSP_STEREO:
740		tmp = -1;
741		*arg_i = (*arg_i)? AFMT_STEREO : 0;
742		if (wrch) {
743			CHN_LOCK(wrch);
744			ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
745			tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
746			CHN_UNLOCK(wrch);
747		}
748		if (rdch && ret == 0) {
749			CHN_LOCK(rdch);
750			ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
751			if (tmp == -1)
752				tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
753			CHN_UNLOCK(rdch);
754		}
755		*arg_i = tmp;
756		break;
757
758    	case SOUND_PCM_WRITE_CHANNELS:
759/*	case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
760		if (*arg_i != 0) {
761			tmp = 0;
762			*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
763	  		if (wrch) {
764				CHN_LOCK(wrch);
765				ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
766				tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
767				CHN_UNLOCK(wrch);
768			}
769			if (rdch && ret == 0) {
770				CHN_LOCK(rdch);
771				ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
772				if (tmp == 0)
773					tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
774				CHN_UNLOCK(rdch);
775			}
776			*arg_i = tmp;
777		} else {
778			chn = wrch ? wrch : rdch;
779			CHN_LOCK(chn);
780			*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
781			CHN_UNLOCK(chn);
782		}
783		break;
784
785    	case SOUND_PCM_READ_CHANNELS:
786		chn = wrch ? wrch : rdch;
787		CHN_LOCK(chn);
788		*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
789		CHN_UNLOCK(chn);
790		break;
791
792    	case SNDCTL_DSP_GETFMTS:	/* returns a mask of supported fmts */
793		chn = wrch ? wrch : rdch;
794		CHN_LOCK(chn);
795		*arg_i = chn_getformats(chn);
796		CHN_UNLOCK(chn);
797		break ;
798
799    	case SNDCTL_DSP_SETFMT:	/* sets _one_ format */
800		if ((*arg_i != AFMT_QUERY)) {
801			tmp = 0;
802			if (wrch) {
803				CHN_LOCK(wrch);
804				ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
805				tmp = wrch->format & ~AFMT_STEREO;
806				CHN_UNLOCK(wrch);
807			}
808			if (rdch && ret == 0) {
809				CHN_LOCK(rdch);
810				ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
811				if (tmp == 0)
812					tmp = rdch->format & ~AFMT_STEREO;
813				CHN_UNLOCK(rdch);
814			}
815			*arg_i = tmp;
816		} else {
817			chn = wrch ? wrch : rdch;
818			CHN_LOCK(chn);
819			*arg_i = chn->format & ~AFMT_STEREO;
820			CHN_UNLOCK(chn);
821		}
822		break;
823
824    	case SNDCTL_DSP_SETFRAGMENT:
825		DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
826		{
827			u_int32_t fragln = (*arg_i) & 0x0000ffff;
828			u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
829			u_int32_t fragsz;
830
831			RANGE(fragln, 4, 16);
832			fragsz = 1 << fragln;
833
834			if (maxfrags == 0)
835				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
836			if (maxfrags < 2)
837				maxfrags = 2;
838			if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
839				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
840
841			DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
842		    	if (rdch) {
843				CHN_LOCK(rdch);
844				ret = chn_setblocksize(rdch, maxfrags, fragsz);
845				maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
846				fragsz = sndbuf_getblksz(rdch->bufsoft);
847				CHN_UNLOCK(rdch);
848			}
849		    	if (wrch && ret == 0) {
850				CHN_LOCK(wrch);
851				ret = chn_setblocksize(wrch, maxfrags, fragsz);
852 				maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
853				fragsz = sndbuf_getblksz(wrch->bufsoft);
854				CHN_UNLOCK(wrch);
855			}
856
857			fragln = 0;
858			while (fragsz > 1) {
859				fragln++;
860				fragsz >>= 1;
861			}
862	    		*arg_i = (maxfrags << 16) | fragln;
863		}
864		break;
865
866    	case SNDCTL_DSP_GETISPACE:
867		/* return the size of data available in the input queue */
868		{
869	    		audio_buf_info *a = (audio_buf_info *)arg;
870	    		if (rdch) {
871	        		struct snd_dbuf *bs = rdch->bufsoft;
872
873				CHN_LOCK(rdch);
874				a->bytes = sndbuf_getready(bs);
875	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
876	        		a->fragstotal = sndbuf_getblkcnt(bs);
877	        		a->fragsize = sndbuf_getblksz(bs);
878				CHN_UNLOCK(rdch);
879	    		}
880		}
881		break;
882
883    	case SNDCTL_DSP_GETOSPACE:
884		/* return space available in the output queue */
885		{
886	    		audio_buf_info *a = (audio_buf_info *)arg;
887	    		if (wrch) {
888	        		struct snd_dbuf *bs = wrch->bufsoft;
889
890				CHN_LOCK(wrch);
891				chn_wrupdate(wrch);
892				a->bytes = sndbuf_getfree(bs);
893	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
894	        		a->fragstotal = sndbuf_getblkcnt(bs);
895	        		a->fragsize = sndbuf_getblksz(bs);
896				CHN_UNLOCK(wrch);
897	    		}
898		}
899		break;
900
901    	case SNDCTL_DSP_GETIPTR:
902		{
903	    		count_info *a = (count_info *)arg;
904	    		if (rdch) {
905	        		struct snd_dbuf *bs = rdch->bufsoft;
906
907				CHN_LOCK(rdch);
908				chn_rdupdate(rdch);
909	        		a->bytes = sndbuf_gettotal(bs);
910	        		a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
911	        		a->ptr = sndbuf_getreadyptr(bs);
912				rdch->blocks = sndbuf_getblocks(bs);
913				CHN_UNLOCK(rdch);
914	    		} else
915				ret = EINVAL;
916		}
917		break;
918
919    	case SNDCTL_DSP_GETOPTR:
920		{
921	    		count_info *a = (count_info *)arg;
922	    		if (wrch) {
923	        		struct snd_dbuf *bs = wrch->bufsoft;
924
925				CHN_LOCK(wrch);
926				chn_wrupdate(wrch);
927	        		a->bytes = sndbuf_gettotal(bs);
928	        		a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
929	        		a->ptr = sndbuf_getreadyptr(bs);
930				wrch->blocks = sndbuf_getblocks(bs);
931				CHN_UNLOCK(wrch);
932	    		} else
933				ret = EINVAL;
934		}
935		break;
936
937    	case SNDCTL_DSP_GETCAPS:
938		*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
939		if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
940			*arg_i |= DSP_CAP_DUPLEX;
941		break;
942
943    	case SOUND_PCM_READ_BITS:
944		chn = wrch ? wrch : rdch;
945		CHN_LOCK(chn);
946        	*arg_i = (chn->format & AFMT_16BIT) ? 16 : 8;
947		CHN_UNLOCK(chn);
948		break;
949
950    	case SNDCTL_DSP_SETTRIGGER:
951		if (rdch) {
952			CHN_LOCK(rdch);
953			rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
954		    	if (*arg_i & PCM_ENABLE_INPUT)
955				chn_start(rdch, 1);
956			else
957				rdch->flags |= CHN_F_NOTRIGGER;
958			CHN_UNLOCK(rdch);
959		}
960		if (wrch) {
961			CHN_LOCK(wrch);
962			wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
963		    	if (*arg_i & PCM_ENABLE_OUTPUT)
964				chn_start(wrch, 1);
965			else
966				wrch->flags |= CHN_F_NOTRIGGER;
967			CHN_UNLOCK(wrch);
968		}
969		break;
970
971    	case SNDCTL_DSP_GETTRIGGER:
972		*arg_i = 0;
973		if (wrch) {
974			CHN_LOCK(wrch);
975			if (wrch->flags & CHN_F_TRIGGERED)
976				*arg_i |= PCM_ENABLE_OUTPUT;
977			CHN_UNLOCK(wrch);
978		}
979		if (rdch) {
980			CHN_LOCK(rdch);
981			if (rdch->flags & CHN_F_TRIGGERED)
982				*arg_i |= PCM_ENABLE_INPUT;
983			CHN_UNLOCK(rdch);
984		}
985		break;
986
987	case SNDCTL_DSP_GETODELAY:
988		if (wrch) {
989			struct snd_dbuf *b = wrch->bufhard;
990	        	struct snd_dbuf *bs = wrch->bufsoft;
991
992			CHN_LOCK(wrch);
993			chn_wrupdate(wrch);
994			*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
995			CHN_UNLOCK(wrch);
996		} else
997			ret = EINVAL;
998		break;
999
1000    	case SNDCTL_DSP_POST:
1001		if (wrch) {
1002			CHN_LOCK(wrch);
1003			wrch->flags &= ~CHN_F_NOTRIGGER;
1004			chn_start(wrch, 1);
1005			CHN_UNLOCK(wrch);
1006		}
1007		break;
1008
1009    	case SNDCTL_DSP_MAPINBUF:
1010    	case SNDCTL_DSP_MAPOUTBUF:
1011    	case SNDCTL_DSP_SETSYNCRO:
1012		/* undocumented */
1013
1014    	case SNDCTL_DSP_SUBDIVIDE:
1015    	case SOUND_PCM_WRITE_FILTER:
1016    	case SOUND_PCM_READ_FILTER:
1017		/* dunno what these do, don't sound important */
1018    	default:
1019		DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
1020		ret = EINVAL;
1021		break;
1022    	}
1023	relchns(i_dev, rdch, wrch, 0);
1024	splx(s);
1025    	return ret;
1026}
1027
1028static int
1029dsp_poll(struct cdev *i_dev, int events, struct thread *td)
1030{
1031	struct pcm_channel *wrch = NULL, *rdch = NULL;
1032	intrmask_t s;
1033	int ret, e;
1034
1035	s = spltty();
1036	ret = 0;
1037	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1038
1039	if (wrch) {
1040		e = (events & (POLLOUT | POLLWRNORM));
1041		if (e)
1042			ret |= chn_poll(wrch, e, td);
1043	}
1044	if (rdch) {
1045		e = (events & (POLLIN | POLLRDNORM));
1046		if (e)
1047			ret |= chn_poll(rdch, e, td);
1048	}
1049	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1050
1051	splx(s);
1052	return ret;
1053}
1054
1055static int
1056dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
1057{
1058	struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1059	intrmask_t s;
1060
1061	if (nprot & PROT_EXEC)
1062		return -1;
1063
1064	s = spltty();
1065	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1066#if 0
1067	/*
1068	 * XXX the linux api uses the nprot to select read/write buffer
1069	 * our vm system doesn't allow this, so force write buffer
1070	 */
1071
1072	if (wrch && (nprot & PROT_WRITE)) {
1073		c = wrch;
1074	} else if (rdch && (nprot & PROT_READ)) {
1075		c = rdch;
1076	} else {
1077		splx(s);
1078		return -1;
1079	}
1080#else
1081	c = wrch;
1082#endif
1083
1084	if (c == NULL) {
1085		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1086		splx(s);
1087		return -1;
1088	}
1089
1090	if (offset >= sndbuf_getsize(c->bufsoft)) {
1091		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1092		splx(s);
1093		return -1;
1094	}
1095
1096	if (!(c->flags & CHN_F_MAPPED))
1097		c->flags |= CHN_F_MAPPED;
1098
1099	*paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
1100	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1101
1102	splx(s);
1103	return 0;
1104}
1105
1106#ifdef USING_DEVFS
1107
1108/*
1109 * Clone logic is this:
1110 * x E X = {dsp, dspW, audio}
1111 * x -> x${sysctl("hw.snd.unit")}
1112 * xN->
1113 *    for i N = 1 to channels of device N
1114 *    	if xN.i isn't busy, return its dev_t
1115 */
1116static void
1117dsp_clone(void *arg, char *name, int namelen, struct cdev **dev)
1118{
1119	struct cdev *pdev;
1120	struct snddev_info *pcm_dev;
1121	struct snddev_channel *pcm_chan;
1122	int i, unit, devtype;
1123	int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1124	char *devnames[3] = {"dsp", "dspW", "audio"};
1125
1126	if (*dev != NULL)
1127		return;
1128	if (pcm_devclass == NULL)
1129		return;
1130
1131	devtype = 0;
1132	unit = -1;
1133	for (i = 0; (i < 3) && (unit == -1); i++) {
1134		devtype = devtypes[i];
1135		if (strcmp(name, devnames[i]) == 0) {
1136			unit = snd_unit;
1137		} else {
1138			if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1139				unit = -1;
1140		}
1141	}
1142	if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1143		return;
1144
1145	pcm_dev = devclass_get_softc(pcm_devclass, unit);
1146
1147	if (pcm_dev == NULL)
1148		return;
1149
1150	SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
1151
1152		switch(devtype) {
1153			case SND_DEV_DSP:
1154				pdev = pcm_chan->dsp_devt;
1155				break;
1156			case SND_DEV_DSP16:
1157				pdev = pcm_chan->dspW_devt;
1158				break;
1159			case SND_DEV_AUDIO:
1160				pdev = pcm_chan->audio_devt;
1161				break;
1162			default:
1163				panic("Unknown devtype %d", devtype);
1164		}
1165
1166		if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1167			*dev = pdev;
1168			return;
1169		}
1170	}
1171}
1172
1173static void
1174dsp_sysinit(void *p)
1175{
1176	dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1177}
1178
1179static void
1180dsp_sysuninit(void *p)
1181{
1182	if (dsp_ehtag != NULL)
1183		EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1184}
1185
1186SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1187SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1188#endif
1189
1190
1191