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