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