dsp.c revision 102172
1139749Simp/*
2122526Ssimokawa * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3122526Ssimokawa * All rights reserved.
4103285Sikob *
5103285Sikob * Redistribution and use in source and binary forms, with or without
6103285Sikob * modification, are permitted provided that the following conditions
7103285Sikob * are met:
8103285Sikob * 1. Redistributions of source code must retain the above copyright
9103285Sikob *    notice, this list of conditions and the following disclaimer.
10103285Sikob * 2. Redistributions in binary form must reproduce the above copyright
11103285Sikob *    notice, this list of conditions and the following disclaimer in the
12103285Sikob *    documentation and/or other materials provided with the distribution.
13103285Sikob *
14103285Sikob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17103285Sikob * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24103285Sikob * SUCH DAMAGE.
25103285Sikob */
26103285Sikob
27103285Sikob#include <sys/param.h>
28103285Sikob#include <sys/queue.h>
29103285Sikob
30103285Sikob#include <dev/sound/pcm/sound.h>
31103285Sikob
32103285SikobSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/dsp.c 102172 2002-08-20 08:02:56Z orion $");
33103285Sikob
34103285Sikob#define OLDPCM_IOCTL
35103285Sikob
36103285Sikobstatic d_open_t dsp_open;
37103285Sikobstatic d_close_t dsp_close;
38103285Sikobstatic d_read_t dsp_read;
39103285Sikobstatic d_write_t dsp_write;
40103285Sikobstatic d_ioctl_t dsp_ioctl;
41103285Sikobstatic d_poll_t dsp_poll;
42127468Ssimokawastatic d_mmap_t dsp_mmap;
43103285Sikob
44103285Sikobstatic struct cdevsw dsp_cdevsw = {
45103285Sikob	/* open */	dsp_open,
46127468Ssimokawa	/* close */	dsp_close,
47117126Sscottl	/* read */	dsp_read,
48117126Sscottl	/* write */	dsp_write,
49117732Ssimokawa	/* ioctl */	dsp_ioctl,
50103285Sikob	/* poll */	dsp_poll,
51127468Ssimokawa	/* mmap */	dsp_mmap,
52112136Ssimokawa	/* strategy */	nostrategy,
53112136Ssimokawa	/* name */	"dsp",
54103285Sikob	/* maj */	SND_CDEV_MAJOR,
55127468Ssimokawa	/* dump */	nodump,
56127468Ssimokawa	/* psize */	nopsize,
57127468Ssimokawa	/* flags */	0,
58127468Ssimokawa};
59127468Ssimokawa
60127468Ssimokawa#ifdef USING_DEVFS
61127468Ssimokawastatic eventhandler_tag dsp_ehtag;
62127468Ssimokawa#endif
63127468Ssimokawa
64127468Ssimokawastatic struct snddev_info *
65127468Ssimokawadsp_get_info(dev_t dev)
66127468Ssimokawa{
67127468Ssimokawa	struct snddev_info *d;
68127468Ssimokawa	int unit;
69127468Ssimokawa
70103285Sikob	unit = PCMUNIT(dev);
71103285Sikob	if (unit >= devclass_get_maxunit(pcm_devclass))
72103285Sikob		return NULL;
73103285Sikob	d = devclass_get_softc(pcm_devclass, unit);
74103285Sikob
75103285Sikob	return d;
76103285Sikob}
77103285Sikob
78103285Sikobstatic u_int32_t
79103285Sikobdsp_get_flags(dev_t dev)
80113584Ssimokawa{
81103285Sikob	device_t bdev;
82120660Ssimokawa	int unit;
83127468Ssimokawa
84103285Sikob	unit = PCMUNIT(dev);
85103285Sikob	if (unit >= devclass_get_maxunit(pcm_devclass))
86103285Sikob		return 0xffffffff;
87103285Sikob	bdev = devclass_get_device(pcm_devclass, unit);
88111615Ssimokawa
89121185Ssimokawa	return pcm_getflags(bdev);
90121185Ssimokawa}
91121185Ssimokawa
92121185Ssimokawastatic void
93121185Ssimokawadsp_set_flags(dev_t dev, u_int32_t flags)
94167622Ssimokawa{
95113584Ssimokawa	device_t bdev;
96113584Ssimokawa	int unit;
97113584Ssimokawa
98103285Sikob	unit = PCMUNIT(dev);
99113584Ssimokawa	if (unit >= devclass_get_maxunit(pcm_devclass))
100111615Ssimokawa		return;
101111615Ssimokawa	bdev = devclass_get_device(pcm_devclass, unit);
102111615Ssimokawa
103111615Ssimokawa	pcm_setflags(bdev, flags);
104130532Sdfr}
105111615Ssimokawa
106111615Ssimokawa/*
107120660Ssimokawa * return the channels channels associated with an open device instance.
108111615Ssimokawa * set the priority if the device is simplex and one direction (only) is
109111615Ssimokawa * specified.
110111615Ssimokawa * lock channels specified.
111103285Sikob */
112120660Ssimokawastatic int
113120660Ssimokawagetchns(dev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
114120660Ssimokawa{
115120660Ssimokawa	struct snddev_info *d;
116103285Sikob	u_int32_t flags;
117103285Sikob
118120660Ssimokawa	flags = dsp_get_flags(dev);
119103285Sikob	d = dsp_get_info(dev);
120103285Sikob	pcm_lock(d);
121120660Ssimokawa	pcm_inprog(d, 1);
122103285Sikob	KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
123103285Sikob		("getchns: read and write both prioritised"));
124111203Ssimokawa
125103285Sikob	if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
126124251Ssimokawa		flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
127111199Ssimokawa		dsp_set_flags(dev, flags);
128122387Ssimokawa	}
129122387Ssimokawa
130122387Ssimokawa	*rdch = dev->si_drv1;
131127468Ssimokawa	*wrch = dev->si_drv2;
132127468Ssimokawa	if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
133103285Sikob		if (prio) {
134103285Sikob			if (*rdch && flags & SD_F_PRIO_WR) {
135227309Sed				dev->si_drv1 = NULL;
136227309Sed				*rdch = pcm_getfakechan(d);
137103285Sikob			} else if (*wrch && flags & SD_F_PRIO_RD) {
138103285Sikob				dev->si_drv2 = NULL;
139103285Sikob				*wrch = pcm_getfakechan(d);
140103285Sikob			}
141103285Sikob		}
142103285Sikob
143121792Ssimokawa		pcm_getfakechan(d)->flags |= CHN_F_BUSY;
144130677Ssimokawa	}
145122387Ssimokawa	pcm_unlock(d);
146122387Ssimokawa
147122387Ssimokawa	if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
148122387Ssimokawa		CHN_LOCK(*rdch);
149127468Ssimokawa	if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
150127468Ssimokawa		CHN_LOCK(*wrch);
151127468Ssimokawa
152127468Ssimokawa	return 0;
153103285Sikob}
154122387Ssimokawa
155122387Ssimokawa/* unlock specified channels */
156122387Ssimokawastatic void
157122387Ssimokawarelchns(dev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
158122387Ssimokawa{
159127468Ssimokawa	struct snddev_info *d;
160127468Ssimokawa
161122387Ssimokawa	d = dsp_get_info(dev);
162103285Sikob	if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
163103285Sikob		CHN_UNLOCK(wrch);
164113584Ssimokawa	if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
165113584Ssimokawa		CHN_UNLOCK(rdch);
166167622Ssimokawa	pcm_lock(d);
167113584Ssimokawa	pcm_inprog(d, -1);
168167622Ssimokawa	pcm_unlock(d);
169113584Ssimokawa}
170103285Sikob
171103285Sikobstatic int
172103285Sikobdsp_open(dev_t i_dev, int flags, int mode, struct thread *td)
173113584Ssimokawa{
174129585Sdfr	struct pcm_channel *rdch, *wrch;
175129585Sdfr	struct snddev_info *d;
176120660Ssimokawa	intrmask_t s;
177103285Sikob	u_int32_t fmt;
178113584Ssimokawa	int devtype;
179103285Sikob
180255871Sscottl	s = spltty();
181103285Sikob	d = dsp_get_info(i_dev);
182113584Ssimokawa	devtype = PCMDEV(i_dev);
183103285Sikob
184103285Sikob	/* decide default format */
185113584Ssimokawa	switch (devtype) {
186103285Sikob	case SND_DEV_DSP16:
187103285Sikob		fmt = AFMT_S16_LE;
188103285Sikob		break;
189114732Ssimokawa
190110336Ssimokawa	case SND_DEV_DSP:
191103285Sikob		fmt = AFMT_U8;
192110336Ssimokawa		break;
193103285Sikob
194103285Sikob	case SND_DEV_AUDIO:
195103285Sikob		fmt = AFMT_MU_LAW;
196103285Sikob		break;
197103285Sikob
198129585Sdfr	case SND_DEV_NORESET:
199114732Ssimokawa		fmt = 0;
200129585Sdfr		break;
201129585Sdfr
202129585Sdfr	case SND_DEV_DSPREC:
203121185Ssimokawa		fmt = AFMT_U8;
204121185Ssimokawa		if (mode & FWRITE) {
205121185Ssimokawa			splx(s);
206121185Ssimokawa			return EINVAL;
207127468Ssimokawa		}
208127468Ssimokawa		break;
209127468Ssimokawa
210129585Sdfr	default:
211103285Sikob		panic("impossible devtype %d", devtype);
212103285Sikob	}
213113584Ssimokawa
214113584Ssimokawa	/* lock snddev so nobody else can monkey with it */
215111615Ssimokawa	pcm_lock(d);
216113584Ssimokawa	if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (i_dev->si_drv1 || i_dev->si_drv2)) {
217103285Sikob		/* simplex device, already open, exit */
218113584Ssimokawa		pcm_unlock(d);
219127468Ssimokawa		splx(s);
220103285Sikob		return EBUSY;
221103285Sikob	}
222103285Sikob
223188756Ssbruno	/*  if we get here, the open request is valid */
224103285Sikob	rdch = i_dev->si_drv1;
225103285Sikob	wrch = i_dev->si_drv2;
226103285Sikob
227103285Sikob	if (flags & FREAD) {
228103285Sikob		/* open for read */
229121185Ssimokawa		if (rdch == NULL) {
230103285Sikob			/* not already open, try to get a channel */
231103285Sikob			if (devtype == SND_DEV_DSPREC)
232129585Sdfr				rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev));
233111615Ssimokawa			else
234111615Ssimokawa				rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1);
235111615Ssimokawa			if (!rdch) {
236111615Ssimokawa				/* no channel available, exit */
237113584Ssimokawa				pcm_unlock(d);
238113584Ssimokawa				splx(s);
239103285Sikob				return EBUSY;
240103285Sikob			}
241103285Sikob			/* got a channel, already locked for us */
242103285Sikob		} else {
243103285Sikob			/* already open for read, exit */
244111615Ssimokawa			pcm_unlock(d);
245103285Sikob			splx(s);
246103285Sikob			return EBUSY;
247103285Sikob		}
248122387Ssimokawa	}
249124145Ssimokawa
250124145Ssimokawa	if (flags & FWRITE) {
251170374Ssimokawa		/* open for write */
252103285Sikob		if (wrch == NULL) {
253170374Ssimokawa			/* not already open, try to get a channel */
254170374Ssimokawa			wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
255122387Ssimokawa			if (!wrch) {
256124169Ssimokawa				/* no channel available */
257124169Ssimokawa				if (rdch && (flags & FREAD)) {
258124169Ssimokawa					/* just opened a read channel, release it */
259121185Ssimokawa					pcm_chnrelease(rdch);
260124169Ssimokawa				}
261121185Ssimokawa				/* exit */
262124169Ssimokawa				pcm_unlock(d);
263127468Ssimokawa				splx(s);
264124169Ssimokawa				return EBUSY;
265124169Ssimokawa			}
266124169Ssimokawa			/* got a channel, already locked for us */
267124169Ssimokawa		} else {
268170807Ssimokawa			/* already open for write */
269124169Ssimokawa			if (rdch && (flags & FREAD)) {
270124169Ssimokawa				/* just opened a read channel, release it */
271124169Ssimokawa				pcm_chnrelease(rdch);
272124169Ssimokawa			}
273121185Ssimokawa			/* exit */
274121185Ssimokawa			pcm_unlock(d);
275124169Ssimokawa			splx(s);
276124169Ssimokawa			return EBUSY;
277124169Ssimokawa		}
278124169Ssimokawa	}
279124169Ssimokawa
280103285Sikob	i_dev->si_drv1 = rdch;
281227293Sed	i_dev->si_drv2 = wrch;
282103285Sikob	pcm_unlock(d);
283103285Sikob	/* finished with snddev, new channels still locked */
284103285Sikob
285103285Sikob	/* bump refcounts, reset and unlock any channels that we just opened */
286111615Ssimokawa	if (rdch) {
287111615Ssimokawa		if (flags & FREAD) {
288103285Sikob	        	if (chn_reset(rdch, fmt)) {
289103285Sikob				pcm_lock(d);
290103285Sikob				pcm_chnrelease(rdch);
291103285Sikob				if (wrch && (flags & FWRITE))
292103285Sikob					pcm_chnrelease(wrch);
293103285Sikob				pcm_unlock(d);
294103285Sikob				splx(s);
295103285Sikob				return ENODEV;
296103285Sikob			}
297103285Sikob			if (flags & O_NONBLOCK)
298103285Sikob				rdch->flags |= CHN_F_NBIO;
299103285Sikob		} else
300103285Sikob			CHN_LOCK(rdch);
301103285Sikob
302103285Sikob		pcm_chnref(rdch, 1);
303103285Sikob	 	CHN_UNLOCK(rdch);
304103285Sikob	}
305103285Sikob	if (wrch) {
306103285Sikob		if (flags & FWRITE) {
307103285Sikob	        	if (chn_reset(wrch, fmt)) {
308103285Sikob				pcm_lock(d);
309103285Sikob				pcm_chnrelease(wrch);
310103285Sikob				if (rdch && (flags & FREAD))
311103285Sikob					pcm_chnrelease(rdch);
312103285Sikob				pcm_unlock(d);
313103285Sikob				splx(s);
314103285Sikob				return ENODEV;
315103285Sikob			}
316103285Sikob			if (flags & O_NONBLOCK)
317103285Sikob				wrch->flags |= CHN_F_NBIO;
318103285Sikob		} else
319103285Sikob			CHN_LOCK(wrch);
320103285Sikob
321103285Sikob		pcm_chnref(wrch, 1);
322103285Sikob	 	CHN_UNLOCK(wrch);
323103285Sikob	}
324103285Sikob	splx(s);
325103285Sikob	return 0;
326103285Sikob}
327103285Sikob
328103285Sikobstatic int
329103285Sikobdsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
330103285Sikob{
331103285Sikob	struct pcm_channel *rdch, *wrch;
332103285Sikob	struct snddev_info *d;
333103285Sikob	intrmask_t s;
334103285Sikob	int exit;
335103285Sikob
336103285Sikob	s = spltty();
337103285Sikob	d = dsp_get_info(i_dev);
338103285Sikob	pcm_lock(d);
339103285Sikob	rdch = i_dev->si_drv1;
340188393Sfjoe	wrch = i_dev->si_drv2;
341103285Sikob
342103285Sikob	exit = 0;
343103285Sikob
344103285Sikob	/* decrement refcount for each channel, exit if nonzero */
345103285Sikob	if (rdch) {
346103285Sikob		CHN_LOCK(rdch);
347103285Sikob		if (pcm_chnref(rdch, -1) > 0) {
348103285Sikob			CHN_UNLOCK(rdch);
349103285Sikob			exit = 1;
350103285Sikob		}
351103285Sikob	}
352103285Sikob	if (wrch) {
353103285Sikob		CHN_LOCK(wrch);
354103285Sikob		if (pcm_chnref(wrch, -1) > 0) {
355103285Sikob			CHN_UNLOCK(wrch);
356103285Sikob			exit = 1;
357103285Sikob		}
358103285Sikob	}
359103285Sikob	if (exit) {
360120660Ssimokawa		pcm_unlock(d);
361111615Ssimokawa		splx(s);
362132432Ssimokawa		return 0;
363111615Ssimokawa	}
364111615Ssimokawa
365132432Ssimokawa	/* both refcounts are zero, abort and release */
366132432Ssimokawa
367103285Sikob	if (pcm_getfakechan(d))
368103285Sikob		pcm_getfakechan(d)->flags = 0;
369103285Sikob
370188756Ssbruno	i_dev->si_drv1 = NULL;
371188756Ssbruno	i_dev->si_drv2 = NULL;
372188756Ssbruno
373103285Sikob	dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
374188756Ssbruno	pcm_unlock(d);
375103285Sikob
376103285Sikob	if (rdch) {
377103285Sikob		chn_abort(rdch); /* won't sleep */
378103285Sikob		rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
379188756Ssbruno		chn_reset(rdch, 0);
380188756Ssbruno		pcm_chnrelease(rdch);
381188756Ssbruno	}
382188756Ssbruno	if (wrch) {
383188756Ssbruno		chn_flush(wrch); /* may sleep */
384108503Ssimokawa		wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
385108503Ssimokawa		chn_reset(wrch, 0);
386103285Sikob		pcm_chnrelease(wrch);
387103285Sikob	}
388103285Sikob
389103285Sikob	splx(s);
390188756Ssbruno	return 0;
391188756Ssbruno}
392188756Ssbruno
393188756Ssbrunostatic int
394188756Ssbrunodsp_read(dev_t i_dev, struct uio *buf, int flag)
395188756Ssbruno{
396188756Ssbruno	struct pcm_channel *rdch, *wrch;
397188756Ssbruno	intrmask_t s;
398188756Ssbruno	int ret;
399103285Sikob
400103285Sikob	s = spltty();
401110184Ssimokawa	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
402110184Ssimokawa
403110184Ssimokawa	KASSERT(rdch, ("dsp_read: nonexistant channel"));
404110184Ssimokawa	KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
405110184Ssimokawa
406110184Ssimokawa	if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
407110184Ssimokawa		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
408110184Ssimokawa		splx(s);
409110184Ssimokawa		return EINVAL;
410110184Ssimokawa	}
411110184Ssimokawa	if (!(rdch->flags & CHN_F_RUNNING))
412110184Ssimokawa		rdch->flags |= CHN_F_RUNNING;
413110184Ssimokawa	ret = chn_read(rdch, buf);
414110184Ssimokawa	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
415110184Ssimokawa
416110184Ssimokawa	splx(s);
417110184Ssimokawa	return ret;
418110184Ssimokawa}
419110184Ssimokawa
420110184Ssimokawastatic int
421110184Ssimokawadsp_write(dev_t i_dev, struct uio *buf, int flag)
422110184Ssimokawa{
423110184Ssimokawa	struct pcm_channel *rdch, *wrch;
424110184Ssimokawa	intrmask_t s;
425110184Ssimokawa	int ret;
426110184Ssimokawa
427110184Ssimokawa	s = spltty();
428110184Ssimokawa	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
429110184Ssimokawa
430110184Ssimokawa	KASSERT(wrch, ("dsp_write: nonexistant channel"));
431110184Ssimokawa	KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
432110184Ssimokawa
433110184Ssimokawa	if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
434110184Ssimokawa		relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
435110184Ssimokawa		splx(s);
436110184Ssimokawa		return EINVAL;
437110184Ssimokawa	}
438110184Ssimokawa	if (!(wrch->flags & CHN_F_RUNNING))
439110184Ssimokawa		wrch->flags |= CHN_F_RUNNING;
440110184Ssimokawa	ret = chn_write(wrch, buf);
441110184Ssimokawa	relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
442110184Ssimokawa
443110184Ssimokawa	splx(s);
444110184Ssimokawa	return ret;
445110184Ssimokawa}
446110184Ssimokawa
447110184Ssimokawastatic int
448110184Ssimokawadsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
449110184Ssimokawa{
450110184Ssimokawa    	struct pcm_channel *wrch, *rdch;
451110184Ssimokawa	struct snddev_info *d;
452110184Ssimokawa	intrmask_t s;
453121185Ssimokawa	int kill;
454121185Ssimokawa    	int ret = 0, *arg_i = (int *)arg, tmp;
455103285Sikob
456108503Ssimokawa	/*
457108503Ssimokawa	 * this is an evil hack to allow broken apps to perform mixer ioctls
458121185Ssimokawa	 * on dsp devices.
459121185Ssimokawa	 */
460121185Ssimokawa
461103285Sikob	if (IOCGROUP(cmd) == 'M') {
462121185Ssimokawa		dev_t pdev;
463114069Ssimokawa
464108503Ssimokawa		pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
465108503Ssimokawa		return mixer_ioctl(pdev, cmd, arg, mode, td);
466108503Ssimokawa	}
467108503Ssimokawa
468108503Ssimokawa    	s = spltty();
469108503Ssimokawa	d = dsp_get_info(i_dev);
470110839Ssimokawa	getchns(i_dev, &rdch, &wrch, 0);
471108642Ssimokawa
472108642Ssimokawa	kill = 0;
473108642Ssimokawa	if (wrch && (wrch->flags & CHN_F_DEAD))
474108503Ssimokawa		kill |= 1;
475108503Ssimokawa	if (rdch && (rdch->flags & CHN_F_DEAD))
476108503Ssimokawa		kill |= 2;
477108503Ssimokawa	if (kill == 3) {
478110839Ssimokawa		relchns(i_dev, rdch, wrch, 0);
479121185Ssimokawa		splx(s);
480121185Ssimokawa		return EINVAL;
481121185Ssimokawa	}
482121185Ssimokawa	if (kill & 1)
483121185Ssimokawa		wrch = NULL;
484110839Ssimokawa	if (kill & 2)
485110839Ssimokawa		rdch = NULL;
486121185Ssimokawa
487121185Ssimokawa    	switch(cmd) {
488121185Ssimokawa#ifdef OLDPCM_IOCTL
489121185Ssimokawa    	/*
490121185Ssimokawa     	 * we start with the new ioctl interface.
491121185Ssimokawa     	 */
492121185Ssimokawa    	case AIONWRITE:	/* how many bytes can write ? */
493121185Ssimokawa/*
494121185Ssimokawa		if (wrch && wrch->bufhard.dl)
495121185Ssimokawa			while (chn_wrfeed(wrch) == 0);
496121185Ssimokawa*/
497187993Ssbruno		*arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
498121185Ssimokawa		break;
499103285Sikob
500121185Ssimokawa    	case AIOSSIZE:     /* set the current blocksize */
501121185Ssimokawa		{
502121185Ssimokawa	    		struct snd_size *p = (struct snd_size *)arg;
503121185Ssimokawa
504121185Ssimokawa			p->play_size = 0;
505121185Ssimokawa			p->rec_size = 0;
506121185Ssimokawa	    		if (wrch) {
507121185Ssimokawa				CHN_LOCK(wrch);
508127468Ssimokawa				chn_setblocksize(wrch, 2, p->play_size);
509121185Ssimokawa				p->play_size = sndbuf_getblksz(wrch->bufsoft);
510121185Ssimokawa				CHN_UNLOCK(wrch);
511121185Ssimokawa			}
512121185Ssimokawa	    		if (rdch) {
513121185Ssimokawa				CHN_LOCK(rdch);
514121185Ssimokawa				chn_setblocksize(rdch, 2, p->rec_size);
515121185Ssimokawa				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
516121185Ssimokawa				CHN_UNLOCK(rdch);
517121185Ssimokawa			}
518121185Ssimokawa		}
519121185Ssimokawa		break;
520121185Ssimokawa    	case AIOGSIZE:	/* get the current blocksize */
521121185Ssimokawa		{
522121185Ssimokawa	    		struct snd_size *p = (struct snd_size *)arg;
523121185Ssimokawa
524121185Ssimokawa	    		if (wrch)
525121185Ssimokawa				p->play_size = sndbuf_getblksz(wrch->bufsoft);
526108503Ssimokawa	    		if (rdch)
527108503Ssimokawa				p->rec_size = sndbuf_getblksz(rdch->bufsoft);
528121185Ssimokawa		}
529121185Ssimokawa		break;
530108503Ssimokawa
531108503Ssimokawa    	case AIOSFMT:
532108503Ssimokawa		{
533110839Ssimokawa	    		snd_chan_param *p = (snd_chan_param *)arg;
534110839Ssimokawa
535110839Ssimokawa	    		if (wrch) {
536121185Ssimokawa				CHN_LOCK(wrch);
537110839Ssimokawa				chn_setformat(wrch, p->play_format);
538121185Ssimokawa				chn_setspeed(wrch, p->play_rate);
539121185Ssimokawa				CHN_UNLOCK(wrch);
540121185Ssimokawa	    		}
541121185Ssimokawa	    		if (rdch) {
542121185Ssimokawa				CHN_LOCK(rdch);
543121185Ssimokawa				chn_setformat(rdch, p->rec_format);
544127468Ssimokawa				chn_setspeed(rdch, p->rec_rate);
545121185Ssimokawa				CHN_UNLOCK(rdch);
546121185Ssimokawa	    		}
547121185Ssimokawa		}
548121185Ssimokawa		/* FALLTHROUGH */
549121185Ssimokawa
550121185Ssimokawa    	case AIOGFMT:
551121185Ssimokawa		{
552121185Ssimokawa	    		snd_chan_param *p = (snd_chan_param *)arg;
553121185Ssimokawa
554188756Ssbruno	    		p->play_rate = wrch? wrch->speed : 0;
555188756Ssbruno	    		p->rec_rate = rdch? rdch->speed : 0;
556188756Ssbruno	    		p->play_format = wrch? wrch->format : 0;
557188756Ssbruno	    		p->rec_format = rdch? rdch->format : 0;
558121185Ssimokawa		}
559121185Ssimokawa		break;
560119196Ssimokawa
561113584Ssimokawa    	case AIOGCAP:     /* get capabilities */
562121185Ssimokawa		{
563121185Ssimokawa	    		snd_capabilities *p = (snd_capabilities *)arg;
564121185Ssimokawa			struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
565113584Ssimokawa			dev_t pdev;
566129585Sdfr
567219543Smarius			if (rdch) {
568219543Smarius				CHN_LOCK(rdch);
569113584Ssimokawa				rcaps = chn_getcaps(rdch);
570113584Ssimokawa			}
571127468Ssimokawa			if (wrch) {
572121185Ssimokawa				CHN_LOCK(wrch);
573121185Ssimokawa				pcaps = chn_getcaps(wrch);
574121185Ssimokawa			}
575113584Ssimokawa	    		p->rate_min = max(rcaps? rcaps->minspeed : 0,
576113584Ssimokawa	                      		  pcaps? pcaps->minspeed : 0);
577113584Ssimokawa	    		p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
578113584Ssimokawa	                      		  pcaps? pcaps->maxspeed : 1000000);
579113584Ssimokawa	    		p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
580113584Ssimokawa	                     		 wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
581113584Ssimokawa			/* XXX bad on sb16 */
582113584Ssimokawa	    		p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
583113584Ssimokawa			 	     (wrch? chn_getformats(wrch) : 0xffffffff);
584113584Ssimokawa			if (rdch && wrch)
585113584Ssimokawa				p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
586113584Ssimokawa			pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
587113584Ssimokawa	    		p->mixers = 1; /* default: one mixer */
588113584Ssimokawa	    		p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
589113584Ssimokawa	    		p->left = p->right = 100;
590113584Ssimokawa			if (wrch)
591113584Ssimokawa				CHN_UNLOCK(wrch);
592121185Ssimokawa			if (rdch)
593121185Ssimokawa				CHN_UNLOCK(rdch);
594113584Ssimokawa		}
595255871Sscottl		break;
596113584Ssimokawa
597113584Ssimokawa    	case AIOSTOP:
598121185Ssimokawa		if (*arg_i == AIOSYNC_PLAY && wrch)
599108503Ssimokawa			*arg_i = chn_abort(wrch);
600103285Sikob		else if (*arg_i == AIOSYNC_CAPTURE && rdch)
601121185Ssimokawa			*arg_i = chn_abort(rdch);
602121185Ssimokawa		else {
603121185Ssimokawa	   	 	printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
604121185Ssimokawa	    		*arg_i = 0;
605121185Ssimokawa		}
606121185Ssimokawa		break;
607121185Ssimokawa
608121185Ssimokawa    	case AIOSYNC:
609121185Ssimokawa		printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
610121185Ssimokawa	    		((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
611121185Ssimokawa		break;
612121185Ssimokawa#endif
613121185Ssimokawa	/*
614121185Ssimokawa	 * here follow the standard ioctls (filio.h etc.)
615121185Ssimokawa	 */
616121185Ssimokawa    	case FIONREAD: /* get # bytes to read */
617121185Ssimokawa/*		if (rdch && rdch->bufhard.dl)
618121185Ssimokawa			while (chn_rdfeed(rdch) == 0);
619121185Ssimokawa*/		*arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0;
620121185Ssimokawa		break;
621121185Ssimokawa
622121185Ssimokawa    	case FIOASYNC: /*set/clear async i/o */
623121185Ssimokawa		DEB( printf("FIOASYNC\n") ; )
624121185Ssimokawa		break;
625121185Ssimokawa
626121185Ssimokawa    	case SNDCTL_DSP_NONBLOCK:
627121185Ssimokawa    	case FIONBIO: /* set/clear non-blocking i/o */
628121185Ssimokawa		if (rdch)
629121185Ssimokawa			rdch->flags &= ~CHN_F_NBIO;
630121185Ssimokawa		if (wrch)
631121185Ssimokawa			wrch->flags &= ~CHN_F_NBIO;
632121185Ssimokawa		if (*arg_i) {
633121185Ssimokawa		    	if (rdch)
634121185Ssimokawa				rdch->flags |= CHN_F_NBIO;
635121185Ssimokawa		    	if (wrch)
636121185Ssimokawa				wrch->flags |= CHN_F_NBIO;
637121185Ssimokawa		}
638121185Ssimokawa		break;
639121185Ssimokawa
640121185Ssimokawa    	/*
641121185Ssimokawa	 * Finally, here is the linux-compatible ioctl interface
642121185Ssimokawa	 */
643121185Ssimokawa#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
644121185Ssimokawa    	case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
645121185Ssimokawa    	case SNDCTL_DSP_GETBLKSIZE:
646121185Ssimokawa		if (wrch)
647121185Ssimokawa			*arg_i = sndbuf_getblksz(wrch->bufsoft);
648121185Ssimokawa		else if (rdch)
649121185Ssimokawa			*arg_i = sndbuf_getblksz(rdch->bufsoft);
650121185Ssimokawa		else
651121185Ssimokawa			*arg_i = 0;
652121185Ssimokawa		break ;
653121185Ssimokawa
654121185Ssimokawa    	case SNDCTL_DSP_SETBLKSIZE:
655121185Ssimokawa		RANGE(*arg_i, 16, 65536);
656114069Ssimokawa		if (wrch) {
657103285Sikob			CHN_LOCK(wrch);
658103285Sikob			chn_setblocksize(wrch, 2, *arg_i);
659103285Sikob			CHN_UNLOCK(wrch);
660103285Sikob		}
661103285Sikob		if (rdch) {
662103285Sikob			CHN_LOCK(rdch);
663114069Ssimokawa			chn_setblocksize(rdch, 2, *arg_i);
664114069Ssimokawa			CHN_UNLOCK(rdch);
665103285Sikob		}
666103285Sikob		break;
667103285Sikob
668114069Ssimokawa    	case SNDCTL_DSP_RESET:
669114069Ssimokawa		DEB(printf("dsp reset\n"));
670114069Ssimokawa		if (wrch)
671114069Ssimokawa			chn_abort(wrch);
672114069Ssimokawa		if (rdch)
673114069Ssimokawa			chn_abort(rdch);
674114069Ssimokawa		break;
675114223Ssimokawa
676114260Ssimokawa    	case SNDCTL_DSP_SYNC:
677114223Ssimokawa		DEB(printf("dsp sync\n"));
678114223Ssimokawa		/* chn_sync may sleep */
679114260Ssimokawa		if (wrch) {
680114260Ssimokawa			CHN_LOCK(wrch);
681114069Ssimokawa			chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
682114069Ssimokawa			CHN_UNLOCK(wrch);
683114069Ssimokawa		}
684114069Ssimokawa		break;
685114069Ssimokawa
686114069Ssimokawa    	case SNDCTL_DSP_SPEED:
687114069Ssimokawa		/* chn_setspeed may sleep */
688114069Ssimokawa		tmp = 0;
689114069Ssimokawa		if (wrch) {
690103285Sikob			CHN_LOCK(wrch);
691111615Ssimokawa			ret = chn_setspeed(wrch, *arg_i);
692103285Sikob			tmp = wrch->speed;
693111615Ssimokawa			CHN_UNLOCK(wrch);
694103285Sikob		}
695111615Ssimokawa		if (rdch && ret == 0) {
696111615Ssimokawa			CHN_LOCK(rdch);
697111615Ssimokawa			ret = chn_setspeed(rdch, *arg_i);
698111615Ssimokawa			if (tmp == 0)
699122387Ssimokawa				tmp = rdch->speed;
700122387Ssimokawa			CHN_UNLOCK(rdch);
701122387Ssimokawa		}
702122387Ssimokawa		*arg_i = tmp;
703122387Ssimokawa		break;
704122387Ssimokawa
705122387Ssimokawa    	case SOUND_PCM_READ_RATE:
706122387Ssimokawa		*arg_i = wrch? wrch->speed : rdch->speed;
707122387Ssimokawa		break;
708122387Ssimokawa
709122387Ssimokawa    	case SNDCTL_DSP_STEREO:
710122387Ssimokawa		tmp = -1;
711122387Ssimokawa		*arg_i = (*arg_i)? AFMT_STEREO : 0;
712122387Ssimokawa		if (wrch) {
713122387Ssimokawa			CHN_LOCK(wrch);
714153706Strhodes			ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
715153706Strhodes			tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
716122387Ssimokawa			CHN_UNLOCK(wrch);
717122387Ssimokawa		}
718122387Ssimokawa		if (rdch && ret == 0) {
719122387Ssimokawa			CHN_LOCK(rdch);
720122387Ssimokawa			ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
721114069Ssimokawa			if (tmp == -1)
722114069Ssimokawa				tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
723111615Ssimokawa			CHN_UNLOCK(rdch);
724111615Ssimokawa		}
725111615Ssimokawa		*arg_i = tmp;
726111615Ssimokawa		break;
727111615Ssimokawa
728188756Ssbruno    	case SOUND_PCM_WRITE_CHANNELS:
729103285Sikob/*	case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
730111615Ssimokawa		if (*arg_i != 0) {
731103285Sikob			tmp = 0;
732111615Ssimokawa			*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
733103285Sikob	  		if (wrch) {
734188756Ssbruno				CHN_LOCK(wrch);
735188756Ssbruno				ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
736188756Ssbruno				tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
737103285Sikob				CHN_UNLOCK(wrch);
738103285Sikob			}
739103285Sikob			if (rdch && ret == 0) {
740121185Ssimokawa				CHN_LOCK(rdch);
741121185Ssimokawa				ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
742111615Ssimokawa				if (tmp == 0)
743103285Sikob					tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
744121185Ssimokawa				CHN_UNLOCK(rdch);
745121185Ssimokawa			}
746121185Ssimokawa			*arg_i = tmp;
747103285Sikob		} else {
748103285Sikob			*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
749170374Ssimokawa		}
750103285Sikob		break;
751111615Ssimokawa
752170374Ssimokawa    	case SOUND_PCM_READ_CHANNELS:
753103285Sikob		*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
754111615Ssimokawa		break;
755188756Ssbruno
756111615Ssimokawa    	case SNDCTL_DSP_GETFMTS:	/* returns a mask of supported fmts */
757111615Ssimokawa		*arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch);
758103285Sikob		break ;
759110336Ssimokawa
760103285Sikob    	case SNDCTL_DSP_SETFMT:	/* sets _one_ format */
761113584Ssimokawa		/* XXX locking */
762111615Ssimokawa		if ((*arg_i != AFMT_QUERY)) {
763103285Sikob			tmp = 0;
764111615Ssimokawa			if (wrch) {
765111615Ssimokawa				CHN_LOCK(wrch);
766111615Ssimokawa				ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
767110336Ssimokawa				tmp = wrch->format & ~AFMT_STEREO;
768110336Ssimokawa				CHN_UNLOCK(wrch);
769111615Ssimokawa			}
770110336Ssimokawa			if (rdch && ret == 0) {
771103285Sikob				CHN_LOCK(rdch);
772103285Sikob				ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
773103285Sikob				if (tmp == 0)
774103285Sikob					tmp = rdch->format & ~AFMT_STEREO;
775103285Sikob				CHN_UNLOCK(rdch);
776103285Sikob			}
777188756Ssbruno			*arg_i = tmp;
778188756Ssbruno		} else
779103285Sikob			*arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
780111615Ssimokawa		break;
781170374Ssimokawa
782103285Sikob    	case SNDCTL_DSP_SETFRAGMENT:
783111615Ssimokawa		/* XXX locking */
784170374Ssimokawa		DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
785111615Ssimokawa		{
786103285Sikob			u_int32_t fragln = (*arg_i) & 0x0000ffff;
787187993Ssbruno			u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
788187993Ssbruno			u_int32_t fragsz;
789187993Ssbruno
790103285Sikob			RANGE(fragln, 4, 16);
791103285Sikob			fragsz = 1 << fragln;
792103285Sikob
793103285Sikob			if (maxfrags == 0)
794103285Sikob				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
795103285Sikob			if (maxfrags < 2) {
796103285Sikob				ret = EINVAL;
797103285Sikob				break;
798103285Sikob			}
799103285Sikob			if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
800103285Sikob				maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
801103285Sikob
802103285Sikob			DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
803103285Sikob		    	if (rdch) {
804103285Sikob				CHN_LOCK(rdch);
805113584Ssimokawa				ret = chn_setblocksize(rdch, maxfrags, fragsz);
806113584Ssimokawa				maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
807113584Ssimokawa				fragsz = sndbuf_getblksz(rdch->bufsoft);
808113584Ssimokawa				CHN_UNLOCK(rdch);
809113584Ssimokawa			}
810113584Ssimokawa		    	if (wrch && ret == 0) {
811113584Ssimokawa				CHN_LOCK(wrch);
812113584Ssimokawa				ret = chn_setblocksize(wrch, maxfrags, fragsz);
813124145Ssimokawa 				maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
814170374Ssimokawa				fragsz = sndbuf_getblksz(wrch->bufsoft);
815124145Ssimokawa				CHN_UNLOCK(wrch);
816124145Ssimokawa			}
817170374Ssimokawa
818124145Ssimokawa			fragln = 0;
819122387Ssimokawa			while (fragsz > 1) {
820113584Ssimokawa				fragln++;
821113584Ssimokawa				fragsz >>= 1;
822113584Ssimokawa			}
823103285Sikob	    		*arg_i = (maxfrags << 16) | fragln;
824103285Sikob		}
825103285Sikob		break;
826103285Sikob
827103285Sikob    	case SNDCTL_DSP_GETISPACE:
828103285Sikob		/* return the size of data available in the input queue */
829103285Sikob		{
830111199Ssimokawa	    		audio_buf_info *a = (audio_buf_info *)arg;
831111199Ssimokawa	    		if (rdch) {
832103285Sikob	        		struct snd_dbuf *bs = rdch->bufsoft;
833170400Ssimokawa
834170400Ssimokawa				CHN_LOCK(rdch);
835170400Ssimokawa				a->bytes = sndbuf_getready(bs);
836170400Ssimokawa	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
837122387Ssimokawa	        		a->fragstotal = sndbuf_getblkcnt(bs);
838122387Ssimokawa	        		a->fragsize = sndbuf_getblksz(bs);
839122387Ssimokawa				CHN_UNLOCK(rdch);
840122387Ssimokawa	    		}
841122387Ssimokawa		}
842122387Ssimokawa		break;
843122387Ssimokawa
844111615Ssimokawa    	case SNDCTL_DSP_GETOSPACE:
845111615Ssimokawa		/* return space available in the output queue */
846103285Sikob		{
847111615Ssimokawa	    		audio_buf_info *a = (audio_buf_info *)arg;
848130532Sdfr	    		if (wrch) {
849103285Sikob	        		struct snd_dbuf *bs = wrch->bufsoft;
850103285Sikob
851110193Ssimokawa				CHN_LOCK(wrch);
852110193Ssimokawa				chn_wrupdate(wrch);
853110193Ssimokawa				a->bytes = sndbuf_getfree(bs);
854121185Ssimokawa	        		a->fragments = a->bytes / sndbuf_getblksz(bs);
855103285Sikob	        		a->fragstotal = sndbuf_getblkcnt(bs);
856110145Ssimokawa	        		a->fragsize = sndbuf_getblksz(bs);
857121185Ssimokawa				CHN_UNLOCK(wrch);
858103285Sikob	    		}
859103285Sikob		}
860103285Sikob		break;
861110193Ssimokawa
862103285Sikob    	case SNDCTL_DSP_GETIPTR:
863188756Ssbruno		{
864188756Ssbruno	    		count_info *a = (count_info *)arg;
865188756Ssbruno	    		if (rdch) {
866188756Ssbruno	        		struct snd_dbuf *bs = rdch->bufsoft;
867103285Sikob
868111615Ssimokawa				CHN_LOCK(rdch);
869103285Sikob				chn_rdupdate(rdch);
870103285Sikob	        		a->bytes = sndbuf_gettotal(bs);
871103285Sikob	        		a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
872103285Sikob	        		a->ptr = sndbuf_getreadyptr(bs);
873103285Sikob				rdch->blocks = sndbuf_getblocks(bs);
874103285Sikob				CHN_UNLOCK(rdch);
875103285Sikob	    		} else
876103285Sikob				ret = EINVAL;
877103285Sikob		}
878103285Sikob		break;
879103285Sikob
880103285Sikob    	case SNDCTL_DSP_GETOPTR:
881103285Sikob		{
882103285Sikob	    		count_info *a = (count_info *)arg;
883103285Sikob	    		if (wrch) {
884103285Sikob	        		struct snd_dbuf *bs = wrch->bufsoft;
885103285Sikob
886111615Ssimokawa				CHN_LOCK(wrch);
887121185Ssimokawa				chn_wrupdate(wrch);
888121185Ssimokawa	        		a->bytes = sndbuf_gettotal(bs);
889103285Sikob	        		a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
890170374Ssimokawa	        		a->ptr = sndbuf_getreadyptr(bs);
891124145Ssimokawa				wrch->blocks = sndbuf_getblocks(bs);
892124145Ssimokawa				CHN_UNLOCK(wrch);
893170374Ssimokawa	    		} else
894103285Sikob				ret = EINVAL;
895103285Sikob		}
896103285Sikob		break;
897103285Sikob
898103285Sikob    	case SNDCTL_DSP_GETCAPS:
899113584Ssimokawa		*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
900103285Sikob		if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
901103285Sikob			*arg_i |= DSP_CAP_DUPLEX;
902113584Ssimokawa		break;
903188756Ssbruno
904103285Sikob    	case SOUND_PCM_READ_BITS:
905113584Ssimokawa        	*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
906113584Ssimokawa		break;
907113584Ssimokawa
908113584Ssimokawa    	case SNDCTL_DSP_SETTRIGGER:
909103285Sikob		if (rdch) {
910103285Sikob			CHN_LOCK(rdch);
911103285Sikob			rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
912103285Sikob		    	if (*arg_i & PCM_ENABLE_INPUT)
913113584Ssimokawa				chn_start(rdch, 1);
914113584Ssimokawa			else
915113584Ssimokawa				rdch->flags |= CHN_F_NOTRIGGER;
916113584Ssimokawa			CHN_UNLOCK(rdch);
917113584Ssimokawa		}
918113584Ssimokawa		if (wrch) {
919113584Ssimokawa			CHN_LOCK(wrch);
920113584Ssimokawa			wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
921113584Ssimokawa		    	if (*arg_i & PCM_ENABLE_OUTPUT)
922170374Ssimokawa				chn_start(wrch, 1);
923113584Ssimokawa			else
924170374Ssimokawa				wrch->flags |= CHN_F_NOTRIGGER;
925113584Ssimokawa		 	CHN_UNLOCK(wrch);
926113584Ssimokawa		}
927113584Ssimokawa		break;
928103285Sikob
929114732Ssimokawa    	case SNDCTL_DSP_GETTRIGGER:
930103285Sikob		*arg_i = 0;
931114732Ssimokawa		if (wrch && wrch->flags & CHN_F_TRIGGERED)
932114732Ssimokawa			*arg_i |= PCM_ENABLE_OUTPUT;
933114732Ssimokawa		if (rdch && rdch->flags & CHN_F_TRIGGERED)
934114732Ssimokawa			*arg_i |= PCM_ENABLE_INPUT;
935114732Ssimokawa		break;
936188756Ssbruno
937188756Ssbruno	case SNDCTL_DSP_GETODELAY:
938114732Ssimokawa		if (wrch) {
939114732Ssimokawa			struct snd_dbuf *b = wrch->bufhard;
940114732Ssimokawa	        	struct snd_dbuf *bs = wrch->bufsoft;
941121185Ssimokawa
942121185Ssimokawa			CHN_LOCK(wrch);
943121185Ssimokawa			chn_wrupdate(wrch);
944114732Ssimokawa			*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
945114732Ssimokawa			CHN_UNLOCK(wrch);
946114732Ssimokawa		} else
947114732Ssimokawa			ret = EINVAL;
948114732Ssimokawa		break;
949114732Ssimokawa
950114732Ssimokawa    	case SNDCTL_DSP_POST:
951114732Ssimokawa		if (wrch) {
952114732Ssimokawa			CHN_LOCK(wrch);
953114732Ssimokawa			wrch->flags &= ~CHN_F_NOTRIGGER;
954188756Ssbruno			chn_start(wrch, 1);
955188756Ssbruno			CHN_UNLOCK(wrch);
956114732Ssimokawa		}
957114732Ssimokawa		break;
958114732Ssimokawa
959167632Ssimokawa    	case SNDCTL_DSP_MAPINBUF:
960120660Ssimokawa    	case SNDCTL_DSP_MAPOUTBUF:
961114732Ssimokawa    	case SNDCTL_DSP_SETSYNCRO:
962114732Ssimokawa		/* undocumented */
963114732Ssimokawa
964114732Ssimokawa    	case SNDCTL_DSP_SUBDIVIDE:
965114732Ssimokawa    	case SOUND_PCM_WRITE_FILTER:
966114732Ssimokawa    	case SOUND_PCM_READ_FILTER:
967114732Ssimokawa		/* dunno what these do, don't sound important */
968114732Ssimokawa    	default:
969114732Ssimokawa		DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
970103285Sikob		ret = EINVAL;
971114732Ssimokawa		break;
972114732Ssimokawa    	}
973103285Sikob	relchns(i_dev, rdch, wrch, 0);
974114732Ssimokawa	splx(s);
975114732Ssimokawa    	return ret;
976188756Ssbruno}
977188756Ssbruno
978103285Sikobstatic int
979114732Ssimokawadsp_poll(dev_t i_dev, int events, struct thread *td)
980113584Ssimokawa{
981103285Sikob	struct pcm_channel *wrch = NULL, *rdch = NULL;
982103285Sikob	intrmask_t s;
983103285Sikob	int ret, e;
984111615Ssimokawa
985111615Ssimokawa	s = spltty();
986111615Ssimokawa	ret = 0;
987121185Ssimokawa	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
988111615Ssimokawa
989111615Ssimokawa	if (wrch) {
990121185Ssimokawa		e = (events & (POLLOUT | POLLWRNORM));
991121185Ssimokawa		if (e)
992121185Ssimokawa			ret |= chn_poll(wrch, e, td);
993121185Ssimokawa	}
994121185Ssimokawa	if (rdch) {
995111615Ssimokawa		e = (events & (POLLIN | POLLRDNORM));
996111615Ssimokawa		if (e)
997111615Ssimokawa			ret |= chn_poll(rdch, e, td);
998103285Sikob	}
999111615Ssimokawa	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1000103285Sikob
1001111615Ssimokawa	splx(s);
1002103285Sikob	return ret;
1003111615Ssimokawa}
1004103285Sikob
1005111615Ssimokawastatic int
1006110269Ssimokawadsp_mmap(dev_t i_dev, vm_offset_t offset, int nprot)
1007188756Ssbruno{
1008188756Ssbruno	struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1009103285Sikob	intrmask_t s;
1010111615Ssimokawa	int ret;
1011111615Ssimokawa
1012111615Ssimokawa	if (nprot & PROT_EXEC)
1013188756Ssbruno		return -1;
1014188756Ssbruno
1015111615Ssimokawa	s = spltty();
1016111615Ssimokawa	getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1017111615Ssimokawa#if 0
1018111615Ssimokawa	/*
1019111615Ssimokawa	 * XXX the linux api uses the nprot to select read/write buffer
1020111615Ssimokawa	 * our vm system doesn't allow this, so force write buffer
1021111615Ssimokawa	 */
1022111615Ssimokawa
1023111615Ssimokawa	if (wrch && (nprot & PROT_WRITE)) {
1024111615Ssimokawa		c = wrch;
1025111615Ssimokawa	} else if (rdch && (nprot & PROT_READ)) {
1026111615Ssimokawa		c = rdch;
1027103285Sikob	} else {
1028103285Sikob		splx(s);
1029103285Sikob		return -1;
1030111615Ssimokawa	}
1031103285Sikob#else
1032111615Ssimokawa	c = wrch;
1033111615Ssimokawa#endif
1034110798Ssimokawa
1035103285Sikob	if (c == NULL) {
1036111615Ssimokawa		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1037111615Ssimokawa		splx(s);
1038111615Ssimokawa		return -1;
1039111615Ssimokawa	}
1040110798Ssimokawa
1041110798Ssimokawa	if (offset >= sndbuf_getsize(c->bufsoft)) {
1042103285Sikob		relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1043188756Ssbruno		splx(s);
1044188756Ssbruno		return -1;
1045103285Sikob	}
1046111615Ssimokawa
1047111615Ssimokawa	if (!(c->flags & CHN_F_MAPPED))
1048111615Ssimokawa		c->flags |= CHN_F_MAPPED;
1049111615Ssimokawa
1050111615Ssimokawa	ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset)));
1051111615Ssimokawa	relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1052103285Sikob
1053111615Ssimokawa	splx(s);
1054111615Ssimokawa	return ret;
1055103285Sikob}
1056103285Sikob
1057103285Sikobint
1058103285Sikobdsp_register(int unit, int channel)
1059170374Ssimokawa{
1060111040Ssimokawa	make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel),
1061111615Ssimokawa		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
1062111615Ssimokawa	make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
1063170374Ssimokawa		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
1064103285Sikob	make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
1065103285Sikob		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
1066111615Ssimokawa
1067111615Ssimokawa	return 0;
1068111615Ssimokawa}
1069111040Ssimokawa
1070122387Ssimokawaint
1071111615Ssimokawadsp_registerrec(int unit, int channel)
1072103285Sikob{
1073103285Sikob	make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSPREC, channel),
1074103285Sikob		 UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel);
1075103285Sikob
1076103285Sikob	return 0;
1077103285Sikob}
1078111615Ssimokawa
1079111615Ssimokawaint
1080103285Sikobdsp_unregister(int unit, int channel)
1081103285Sikob{
1082111615Ssimokawa	dev_t pdev;
1083111615Ssimokawa
1084103285Sikob	pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, channel));
1085188756Ssbruno	destroy_dev(pdev);
1086188756Ssbruno	pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, channel));
1087103285Sikob	destroy_dev(pdev);
1088113584Ssimokawa	pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, channel));
1089111199Ssimokawa	destroy_dev(pdev);
1090103285Sikob
1091249468Smav	return 0;
1092111615Ssimokawa}
1093111615Ssimokawa
1094103285Sikobint
1095111199Ssimokawadsp_unregisterrec(int unit, int channel)
1096111199Ssimokawa{
1097111199Ssimokawa	dev_t pdev;
1098111199Ssimokawa
1099111199Ssimokawa	pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSPREC, channel));
1100111199Ssimokawa	destroy_dev(pdev);
1101111615Ssimokawa
1102111199Ssimokawa	return 0;
1103111199Ssimokawa}
1104111199Ssimokawa
1105111615Ssimokawa#ifdef USING_DEVFS
1106103285Sikobstatic void
1107103285Sikobdsp_clone(void *arg, char *name, int namelen, dev_t *dev)
1108103285Sikob{
1109103285Sikob	dev_t pdev;
1110103285Sikob	int i, cont, unit, devtype;
1111103285Sikob	int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1112103285Sikob	char *devnames[3] = {"dsp", "dspW", "audio"};
1113103285Sikob
1114103285Sikob	if (*dev != NODEV)
1115103285Sikob		return;
1116188756Ssbruno	if (pcm_devclass == NULL)
1117188756Ssbruno		return;
1118103285Sikob
1119114732Ssimokawa	devtype = 0;
1120188756Ssbruno	unit = -1;
1121188756Ssbruno	for (i = 0; (i < 3) && (unit == -1); i++) {
1122114732Ssimokawa		devtype = devtypes[i];
1123114732Ssimokawa		if (strcmp(name, devnames[i]) == 0) {
1124113584Ssimokawa			unit = snd_unit;
1125111615Ssimokawa		} else {
1126170374Ssimokawa			if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1127111615Ssimokawa				unit = -1;
1128111615Ssimokawa		}
1129170374Ssimokawa	}
1130111615Ssimokawa	if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1131103285Sikob		return;
1132103285Sikob
1133103285Sikob	cont = 1;
1134110336Ssimokawa	for (i = 0; cont; i++) {
1135103285Sikob		pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, devtype, i));
1136103285Sikob		if (pdev->si_flags & SI_NAMED) {
1137103285Sikob			if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1138103285Sikob				*dev = pdev;
1139103285Sikob				return;
1140188756Ssbruno			}
1141188756Ssbruno		} else {
1142103285Sikob			cont = 0;
1143103285Sikob		}
1144103285Sikob	}
1145103285Sikob}
1146115788Ssimokawa
1147167632Ssimokawastatic void
1148110336Ssimokawadsp_sysinit(void *p)
1149167632Ssimokawa{
1150120660Ssimokawa	dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1151103285Sikob}
1152103285Sikob
1153111615Ssimokawastatic void
1154103285Sikobdsp_sysuninit(void *p)
1155103285Sikob{
1156103285Sikob	if (dsp_ehtag != NULL)
1157103285Sikob		EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1158103285Sikob}
1159103285Sikob
1160103285SikobSYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1161103285SikobSYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1162103285Sikob#endif
1163188756Ssbruno
1164188756Ssbruno
1165103285Sikob