dsp.c revision 64029
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 * $FreeBSD: head/sys/dev/sound/pcm/dsp.c 64029 2000-07-30 19:36:13Z cg $
27 */
28
29#include <sys/param.h>
30#include <sys/queue.h>
31
32#include <dev/sound/pcm/sound.h>
33
34#define OLDPCM_IOCTL
35
36static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch);
37
38static pcm_channel *
39allocchn(snddev_info *d, int direction)
40{
41	pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
42	int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
43	for (i = 0; i < cnt; i++) {
44		if (!(chns[i].flags & (CHN_F_BUSY | CHN_F_DEAD))) {
45			chns[i].flags |= CHN_F_BUSY;
46			return &chns[i];
47		}
48	}
49	return NULL;
50}
51
52static int
53getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
54{
55	KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
56		("getchns: read and write both prioritised"));
57
58	if ((d->flags & SD_F_SIMPLEX) && (d->flags & SD_F_PRIO_SET)) {
59		*rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
60		*wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
61		d->fakechan.flags |= CHN_F_BUSY;
62	} else {
63		*rdch = d->arec[chan];
64		*wrch = d->aplay[chan];
65	}
66	return 0;
67}
68
69static void
70setchns(snddev_info *d, int chan)
71{
72	KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
73		("getchns: read and write both prioritised"));
74	d->flags |= SD_F_DIR_SET;
75	if (d->swap) d->swap(d->devinfo, (d->flags & SD_F_PRIO_WR)? PCMDIR_PLAY : PCMDIR_REC);
76}
77
78int
79dsp_open(snddev_info *d, int chan, int oflags, int devtype)
80{
81	pcm_channel *rdch, *wrch;
82	u_int32_t fmt;
83
84	if (chan >= d->chancount) return ENODEV;
85	if ((d->flags & SD_F_SIMPLEX) && (d->ref[chan] > 0)) return EBUSY;
86	rdch = d->arec[chan];
87	wrch = d->aplay[chan];
88	if (oflags & FREAD) {
89		if (rdch == NULL) {
90			rdch = allocchn(d, PCMDIR_REC);
91			if (!rdch) return EBUSY;
92		} else return EBUSY;
93	}
94	if (oflags & FWRITE) {
95		if (wrch == NULL) {
96			wrch = allocchn(d, PCMDIR_PLAY);
97			if (!wrch) {
98				if (rdch && (oflags & FREAD))
99					rdch->flags &= ~CHN_F_BUSY;
100				return EBUSY;
101			}
102		} else return EBUSY;
103	}
104	d->aplay[chan] = wrch;
105	d->arec[chan] = rdch;
106	d->ref[chan]++;
107	switch (devtype) {
108	case SND_DEV_DSP16:
109		fmt = AFMT_S16_LE;
110		break;
111
112	case SND_DEV_DSP:
113		fmt = AFMT_U8;
114		break;
115
116	case SND_DEV_AUDIO:
117		fmt = AFMT_MU_LAW;
118		break;
119
120	case SND_DEV_NORESET:
121		fmt = 0;
122		break;
123
124	default:
125		return ENXIO;
126	}
127
128	if (rdch && (oflags & FREAD)) {
129	        chn_reset(rdch, fmt);
130		if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
131	}
132	if (wrch && (oflags & FWRITE)) {
133	        chn_reset(wrch, fmt);
134		if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
135	}
136	return 0;
137}
138
139int
140dsp_close(snddev_info *d, int chan, int devtype)
141{
142	pcm_channel *rdch, *wrch;
143
144#if 0
145	/* enable this if/when every close() is propagated here */
146	d->ref[chan]--;
147	if (d->ref[chan]) return 0;
148#endif
149	d->flags &= ~SD_F_TRANSIENT;
150	rdch = d->arec[chan];
151	wrch = d->aplay[chan];
152
153	if (rdch) {
154		chn_abort(rdch);
155		rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
156		chn_reset(rdch, 0);
157	}
158	if (wrch) {
159		chn_flush(wrch);
160		wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
161		chn_reset(wrch, 0);
162	}
163	d->aplay[chan] = NULL;
164	d->arec[chan] = NULL;
165	return 0;
166}
167
168int
169dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
170{
171	pcm_channel *rdch, *wrch;
172
173	if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
174	if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
175	getchns(d, chan, &rdch, &wrch);
176	KASSERT(rdch, ("dsp_read: nonexistant channel"));
177	KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
178	if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL;
179	if (!(rdch->flags & CHN_F_RUNNING))
180		rdch->flags |= CHN_F_RUNNING;
181	return chn_read(rdch, buf);
182}
183
184int
185dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
186{
187	pcm_channel *rdch, *wrch;
188
189	if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
190	if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
191	getchns(d, chan, &rdch, &wrch);
192	KASSERT(wrch, ("dsp_write: nonexistant channel"));
193	KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
194	if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL;
195	if (!(wrch->flags & CHN_F_RUNNING))
196		wrch->flags |= CHN_F_RUNNING;
197	return chn_write(wrch, buf);
198}
199
200int
201dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
202{
203    	int ret = 0, *arg_i = (int *)arg;
204    	u_long s;
205    	pcm_channel *wrch = NULL, *rdch = NULL;
206
207	rdch = d->arec[chan];
208	wrch = d->aplay[chan];
209
210	if (rdch && (rdch->flags & CHN_F_DEAD))
211		rdch = NULL;
212	if (wrch && (wrch->flags & CHN_F_DEAD))
213		wrch = NULL;
214	if (!(rdch || wrch))
215		return EINVAL;
216    	/*
217     	 * all routines are called with int. blocked. Make sure that
218     	 * ints are re-enabled when calling slow or blocking functions!
219     	 */
220    	s = spltty();
221    	switch(cmd) {
222#ifdef OLDPCM_IOCTL
223    	/*
224     	 * we start with the new ioctl interface.
225     	 */
226    	case AIONWRITE:	/* how many bytes can write ? */
227		if (wrch && wrch->buffer.dl)
228			while (chn_wrfeed(wrch) > 0);
229		*arg_i = wrch? wrch->buffer2nd.fl : 0;
230		break;
231
232    	case AIOSSIZE:     /* set the current blocksize */
233		{
234	    		struct snd_size *p = (struct snd_size *)arg;
235	    		if (wrch)
236				chn_setblocksize(wrch, 2, p->play_size);
237	    		if (rdch)
238				chn_setblocksize(rdch, 2, p->rec_size);
239		}
240		/* FALLTHROUGH */
241    	case AIOGSIZE:	/* get the current blocksize */
242		{
243	    		struct snd_size *p = (struct snd_size *)arg;
244	    		if (wrch) p->play_size = wrch->buffer2nd.blksz;
245	    		if (rdch) p->rec_size = rdch->buffer2nd.blksz;
246		}
247		break;
248
249    	case AIOSFMT:
250		{
251	    		snd_chan_param *p = (snd_chan_param *)arg;
252	    		if (wrch) {
253				chn_setformat(wrch, p->play_format);
254				chn_setspeed(wrch, p->play_rate);
255	    		}
256	    		if (rdch) {
257				chn_setformat(rdch, p->rec_format);
258				chn_setspeed(rdch, p->rec_rate);
259	    		}
260		}
261		/* FALLTHROUGH */
262
263    	case AIOGFMT:
264		{
265	    		snd_chan_param *p = (snd_chan_param *)arg;
266	    		p->play_rate = wrch? wrch->speed : 0;
267	    		p->rec_rate = rdch? rdch->speed : 0;
268	    		p->play_format = wrch? wrch->format : 0;
269	    		p->rec_format = rdch? rdch->format : 0;
270		}
271		break;
272
273    	case AIOGCAP:     /* get capabilities */
274		{
275	    		snd_capabilities *p = (snd_capabilities *)arg;
276			pcmchan_caps *pcaps = NULL, *rcaps = NULL;
277			if (rdch) rcaps = chn_getcaps(rdch);
278			if (wrch) pcaps = chn_getcaps(wrch);
279	    		p->rate_min = max(rcaps? rcaps->minspeed : 0,
280	                      		  pcaps? pcaps->minspeed : 0);
281	    		p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
282	                      		  pcaps? pcaps->maxspeed : 1000000);
283	    		p->bufsize = min(rdch? rdch->buffer2nd.bufsize : 1000000,
284	                     		 wrch? wrch->buffer2nd.bufsize : 1000000);
285			/* XXX bad on sb16 */
286	    		p->formats = (rcaps? rcaps->formats : 0xffffffff) &
287			 	     (pcaps? pcaps->formats : 0xffffffff);
288			if (rdch && wrch)
289				p->formats |= (d->flags & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
290	    		p->mixers = 1; /* default: one mixer */
291	    		p->inputs = d->mixer.devs;
292	    		p->left = p->right = 100;
293		}
294		break;
295
296    	case AIOSTOP:
297		if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch);
298		else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch);
299		else {
300	   	 	printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
301	    		*arg_i = 0;
302		}
303		break;
304
305    	case AIOSYNC:
306		printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
307	    		((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
308		break;
309#endif
310	/*
311	 * here follow the standard ioctls (filio.h etc.)
312	 */
313    	case FIONREAD: /* get # bytes to read */
314		if (rdch && rdch->buffer.dl)
315			while (chn_rdfeed(rdch) > 0);
316		*arg_i = rdch? rdch->buffer2nd.rl : 0;
317		break;
318
319    	case FIOASYNC: /*set/clear async i/o */
320		DEB( printf("FIOASYNC\n") ; )
321		break;
322
323    	case SNDCTL_DSP_NONBLOCK:
324    	case FIONBIO: /* set/clear non-blocking i/o */
325		if (rdch) rdch->flags &= ~CHN_F_NBIO;
326		if (wrch) wrch->flags &= ~CHN_F_NBIO;
327		if (*arg_i) {
328		    	if (rdch) rdch->flags |= CHN_F_NBIO;
329		    	if (wrch) wrch->flags |= CHN_F_NBIO;
330		}
331		break;
332
333    	/*
334	 * Finally, here is the linux-compatible ioctl interface
335	 */
336#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
337    	case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
338    	case SNDCTL_DSP_GETBLKSIZE:
339		if (wrch)
340			*arg_i = wrch->buffer2nd.blksz;
341		else if (rdch)
342			*arg_i = rdch->buffer2nd.blksz;
343		else
344			*arg_i = 0;
345		break ;
346
347    	case SNDCTL_DSP_SETBLKSIZE:
348		RANGE(*arg_i, 16, 65536);
349		if (wrch) chn_setblocksize(wrch, 2, *arg_i);
350		if (rdch) chn_setblocksize(rdch, 2, *arg_i);
351		break;
352
353    	case SNDCTL_DSP_RESET:
354		DEB(printf("dsp reset\n"));
355		splx(s);
356		if (wrch) chn_abort(wrch);
357		if (rdch) chn_abort(rdch);
358		break;
359
360    	case SNDCTL_DSP_SYNC:
361		DEB(printf("dsp sync\n"));
362		splx(s);
363		if (wrch) chn_sync(wrch, wrch->buffer2nd.bufsize - 4);
364		break;
365
366    	case SNDCTL_DSP_SPEED:
367		splx(s);
368		if (wrch)
369			ret = chn_setspeed(wrch, *arg_i);
370		if (rdch && ret == 0)
371			ret = chn_setspeed(rdch, *arg_i);
372		/* fallthru */
373
374    	case SOUND_PCM_READ_RATE:
375		*arg_i = wrch? wrch->speed : rdch->speed;
376		break;
377
378    	case SNDCTL_DSP_STEREO:
379		splx(s);
380		if (wrch)
381			ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
382					((*arg_i)? AFMT_STEREO : 0));
383		if (rdch && ret == 0)
384			ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
385				        ((*arg_i)? AFMT_STEREO : 0));
386		*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
387		break;
388
389    	case SOUND_PCM_WRITE_CHANNELS:
390/*	case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
391		splx(s);
392		if (*arg_i == 1 || *arg_i == 2) {
393	  		if (wrch)
394				ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
395					((*arg_i == 2)? AFMT_STEREO : 0));
396			if (rdch && ret == 0)
397				ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
398				        ((*arg_i == 2)? AFMT_STEREO : 0));
399			*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
400		} else
401			*arg_i = 0;
402		break;
403
404    	case SOUND_PCM_READ_CHANNELS:
405		*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
406		break;
407
408    	case SNDCTL_DSP_GETFMTS:	/* returns a mask of supported fmts */
409		*arg_i = wrch? chn_getcaps(wrch)->formats : chn_getcaps(rdch)->formats;
410		break ;
411
412    	case SNDCTL_DSP_SETFMT:	/* sets _one_ format */
413		splx(s);
414		if (wrch)
415			ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
416		if (rdch && ret == 0)
417			ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
418		*arg_i = (wrch? wrch->format: rdch->format) & ~AFMT_STEREO;
419		break;
420
421    	case SNDCTL_DSP_SUBDIVIDE:
422		/* XXX watch out, this is RW! */
423		printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");
424		break;
425
426    	case SNDCTL_DSP_SETFRAGMENT:
427		DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
428		{
429			pcm_channel *c = wrch? wrch : rdch;
430			u_int32_t fragln = (*arg_i) & 0x0000ffff;
431			u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
432			u_int32_t fragsz;
433
434			RANGE(fragln, 4, 16);
435			fragsz = 1 << fragln;
436
437			if (maxfrags == 0)
438				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
439			if (maxfrags < 2) {
440				ret = EINVAL;
441				break;
442			}
443			if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
444				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
445
446			DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
447		    	if (rdch)
448				ret = chn_setblocksize(rdch, maxfrags, fragsz);
449		    	if (wrch && ret == 0)
450				ret = chn_setblocksize(wrch, maxfrags, fragsz);
451
452			fragsz = c->buffer2nd.blksz;
453			fragln = 0;
454			while (fragsz > 1) {
455				fragln++;
456				fragsz >>= 1;
457			}
458	    		*arg_i = (c->buffer2nd.blkcnt << 16) | fragln;
459		}
460		break;
461
462    	case SNDCTL_DSP_GETISPACE: /* XXX Space for reading? Makes no sense... */
463		/* return the size of data available in the input queue */
464		{
465	    		audio_buf_info *a = (audio_buf_info *)arg;
466	    		if (rdch) {
467	        		snd_dbuf *b = &rdch->buffer;
468	        		snd_dbuf *bs = &rdch->buffer2nd;
469				if (b->dl && !(rdch->flags & CHN_F_MAPPED))
470					/*
471					 * Suck up the secondary and DMA buffer.
472					 * chn_rdfeed*() takes care of the alignment.
473					 */
474					while (chn_rdfeed(rdch) > 0);
475				a->bytes = bs->rl;
476	        		a->fragments = a->bytes / rdch->buffer2nd.blksz;
477	        		a->fragstotal = rdch->buffer2nd.blkcnt;
478	        		a->fragsize = rdch->buffer2nd.blksz;
479	    		}
480		}
481		break;
482
483    	case SNDCTL_DSP_GETOSPACE:
484		/* return space available in the output queue */
485		{
486	    		audio_buf_info *a = (audio_buf_info *)arg;
487	    		if (wrch) {
488	        		snd_dbuf *b = &wrch->buffer;
489	        		snd_dbuf *bs = &wrch->buffer2nd;
490				if (b->dl && !(wrch->flags & CHN_F_MAPPED)) {
491					/*
492					 * Fill up the secondary and DMA buffer.
493					 * chn_wrfeed*() takes care of the alignment.
494					 * Check for underflow before writing into the buffers.
495					 */
496					chn_checkunderflow(wrch);
497					while (chn_wrfeed(wrch) > 0);
498				}
499				a->bytes = bs->fl;
500	        		a->fragments = a->bytes / wrch->buffer2nd.blksz;
501	        		a->fragstotal = wrch->buffer2nd.blkcnt;
502	        		a->fragsize = wrch->buffer2nd.blksz;
503	    		}
504		}
505		break;
506
507    	case SNDCTL_DSP_GETIPTR:
508		{
509	    		count_info *a = (count_info *)arg;
510	    		if (rdch) {
511	        		snd_dbuf *b = &rdch->buffer;
512	        		snd_dbuf *bs = &rdch->buffer2nd;
513	        		if (b->dl && !(rdch->flags & CHN_F_MAPPED))
514					/*
515					 * Suck up the secondary and DMA buffer.
516					 * chn_rdfeed*() takes care of the alignment.
517					 */
518					while (chn_rdfeed(rdch) > 0);
519	        		a->bytes = bs->total;
520	        		a->blocks = rdch->blocks;
521	        		a->ptr = bs->rp;
522				rdch->blocks = 0;
523	    		} else ret = EINVAL;
524		}
525		break;
526
527    	case SNDCTL_DSP_GETOPTR:
528		{
529	    		count_info *a = (count_info *)arg;
530	    		if (wrch) {
531    	        		snd_dbuf *b = &wrch->buffer;
532	        		snd_dbuf *bs = &wrch->buffer2nd;
533				if (b->dl && !(wrch->flags & CHN_F_MAPPED)) {
534					/*
535					 * Fill up the secondary and DMA buffer.
536					 * chn_wrfeed*() takes care of the alignment.
537					 * Check for underflow before writing into the buffers.
538					 */
539					chn_checkunderflow(wrch);
540					while (chn_wrfeed(wrch) > 0);
541				}
542	        		a->bytes = bs->total;
543	        		a->blocks = wrch->blocks;
544	        		a->ptr = bs->rp;
545				wrch->blocks = 0;
546	    		} else ret = EINVAL;
547		}
548		break;
549
550    	case SNDCTL_DSP_GETCAPS:
551		*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
552		if (rdch && wrch && !(d->flags & SD_F_SIMPLEX))
553			*arg_i |= DSP_CAP_DUPLEX;
554		break;
555
556    	case SOUND_PCM_READ_BITS:
557        	*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
558		break;
559
560    	case SNDCTL_DSP_SETTRIGGER:
561		if (rdch) {
562			rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
563		    	if (*arg_i & PCM_ENABLE_INPUT)
564				rdch->flags |= CHN_F_TRIGGERED;
565			else
566				rdch->flags |= CHN_F_NOTRIGGER;
567			chn_intr(rdch);
568		}
569		if (wrch) {
570			wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
571		    	if (*arg_i & PCM_ENABLE_OUTPUT)
572				wrch->flags |= CHN_F_TRIGGERED;
573			else
574				wrch->flags |= CHN_F_NOTRIGGER;
575			chn_intr(wrch);
576		}
577		break;
578
579    	case SNDCTL_DSP_GETTRIGGER:
580		*arg_i = 0;
581		if (wrch && wrch->flags & CHN_F_TRIGGERED)
582			*arg_i |= PCM_ENABLE_OUTPUT;
583		if (rdch && rdch->flags & CHN_F_TRIGGERED)
584			*arg_i |= PCM_ENABLE_INPUT;
585		break;
586
587	case SNDCTL_DSP_GETODELAY:
588		if (wrch) {
589			snd_dbuf *b = &wrch->buffer;
590			if (b->dl) {
591				chn_checkunderflow(wrch);
592				if (!(wrch->flags & CHN_F_MAPPED))
593					while (chn_wrfeed(wrch) > 0);
594			}
595			*arg = b->total;
596		} else
597			ret = EINVAL;
598		break;
599
600    	case SNDCTL_DSP_MAPINBUF:
601    	case SNDCTL_DSP_MAPOUTBUF:
602    	case SNDCTL_DSP_SETSYNCRO:
603		/* undocumented */
604
605    	case SNDCTL_DSP_POST:
606    	case SOUND_PCM_WRITE_FILTER:
607    	case SOUND_PCM_READ_FILTER:
608		/* dunno what these do, don't sound important */
609    	default:
610		DEB(printf("default ioctl chan%d fn 0x%08lx fail\n", chan, cmd));
611		ret = EINVAL;
612		break;
613    	}
614    	splx(s);
615    	return ret;
616}
617
618int
619dsp_poll(snddev_info *d, int chan, int events, struct proc *p)
620{
621	int ret = 0, e;
622	pcm_channel *wrch = NULL, *rdch = NULL;
623
624	getchns(d, chan, &rdch, &wrch);
625	e = events & (POLLOUT | POLLWRNORM);
626	if (wrch && e) ret |= chn_poll(wrch, e, p);
627	e = events & (POLLIN | POLLRDNORM);
628	if (rdch && e) ret |= chn_poll(rdch, e, p);
629	return ret;
630}
631
632int
633dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
634{
635	pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
636
637	getchns(d, chan, &rdch, &wrch);
638	/* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
639	if (1 || (wrch && (nprot & PROT_WRITE)))
640		c = wrch;
641	else if (rdch && (nprot & PROT_READ))
642		c = rdch;
643	if (c && (c->format == c->buffer.fmt)) {
644		c->flags |= CHN_F_MAPPED;
645		return atop(vtophys(c->buffer2nd.buf + offset));
646	} else
647		return -1;
648}
649
650