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