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.7 2004/05/21 01:14:27 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.7 2004/05/21 01:14:27 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 = make_adhoc_dev(&dsp_cdevsw,
457: PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
458: return mixer_ioctl(pdev, cmd, arg, mode, td);
459: }
460:
461: s = spltty();
462: d = dsp_get_info(i_dev);
463: getchns(i_dev, &rdch, &wrch, 0);
464:
465: kill = 0;
466: if (wrch && (wrch->flags & CHN_F_DEAD))
467: kill |= 1;
468: if (rdch && (rdch->flags & CHN_F_DEAD))
469: kill |= 2;
470: if (kill == 3) {
471: relchns(i_dev, rdch, wrch, 0);
472: splx(s);
473: return EINVAL;
474: }
475: if (kill & 1)
476: wrch = NULL;
477: if (kill & 2)
478: rdch = NULL;
479:
480: switch(cmd) {
481: #ifdef OLDPCM_IOCTL
482: /*
483: * we start with the new ioctl interface.
484: */
485: case AIONWRITE: /* how many bytes can write ? */
486: /*
487: if (wrch && wrch->bufhard.dl)
488: while (chn_wrfeed(wrch) == 0);
489: */
490: *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
491: break;
492:
493: case AIOSSIZE: /* set the current blocksize */
494: {
495: struct snd_size *p = (struct snd_size *)arg;
496:
497: p->play_size = 0;
498: p->rec_size = 0;
499: if (wrch) {
500: CHN_LOCK(wrch);
501: chn_setblocksize(wrch, 2, p->play_size);
502: p->play_size = sndbuf_getblksz(wrch->bufsoft);
503: CHN_UNLOCK(wrch);
504: }
505: if (rdch) {
506: CHN_LOCK(rdch);
507: chn_setblocksize(rdch, 2, p->rec_size);
508: p->rec_size = sndbuf_getblksz(rdch->bufsoft);
509: CHN_UNLOCK(rdch);
510: }
511: }
512: break;
513: case AIOGSIZE: /* get the current blocksize */
514: {
515: struct snd_size *p = (struct snd_size *)arg;
516:
517: if (wrch)
518: p->play_size = sndbuf_getblksz(wrch->bufsoft);
519: if (rdch)
520: p->rec_size = sndbuf_getblksz(rdch->bufsoft);
521: }
522: break;
523:
524: case AIOSFMT:
525: {
526: snd_chan_param *p = (snd_chan_param *)arg;
527:
528: if (wrch) {
529: CHN_LOCK(wrch);
530: chn_setformat(wrch, p->play_format);
531: chn_setspeed(wrch, p->play_rate);
532: CHN_UNLOCK(wrch);
533: }
534: if (rdch) {
535: CHN_LOCK(rdch);
536: chn_setformat(rdch, p->rec_format);
537: chn_setspeed(rdch, p->rec_rate);
538: CHN_UNLOCK(rdch);
539: }
540: }
541: /* FALLTHROUGH */
542:
543: case AIOGFMT:
544: {
545: snd_chan_param *p = (snd_chan_param *)arg;
546:
547: p->play_rate = wrch? wrch->speed : 0;
548: p->rec_rate = rdch? rdch->speed : 0;
549: p->play_format = wrch? wrch->format : 0;
550: p->rec_format = rdch? rdch->format : 0;
551: }
552: break;
553:
554: case AIOGCAP: /* get capabilities */
555: {
556: snd_capabilities *p = (snd_capabilities *)arg;
557: struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
558: dev_t pdev;
559:
560: if (rdch) {
561: CHN_LOCK(rdch);
562: rcaps = chn_getcaps(rdch);
563: }
564: if (wrch) {
565: CHN_LOCK(wrch);
566: pcaps = chn_getcaps(wrch);
567: }
568: p->rate_min = max(rcaps? rcaps->minspeed : 0,
569: pcaps? pcaps->minspeed : 0);
570: p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
571: pcaps? pcaps->maxspeed : 1000000);
572: p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
573: wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
574: /* XXX bad on sb16 */
575: p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
576: (wrch? chn_getformats(wrch) : 0xffffffff);
577: if (rdch && wrch)
578: p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
579: pdev = make_adhoc_dev(&dsp_cdevsw, PCMMKMINOR(PCMUNIT(i_dev), SND_DEV_CTL, 0));
580: p->mixers = 1; /* default: one mixer */
581: p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
582: p->left = p->right = 100;
583: if (wrch)
584: CHN_UNLOCK(wrch);
585: if (rdch)
586: CHN_UNLOCK(rdch);
587: }
588: break;
589:
590: case AIOSTOP:
591: if (*arg_i == AIOSYNC_PLAY && wrch)
592: *arg_i = chn_abort(wrch);
593: else if (*arg_i == AIOSYNC_CAPTURE && rdch)
594: *arg_i = chn_abort(rdch);
595: else {
596: printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
597: *arg_i = 0;
598: }
599: break;
600:
601: case AIOSYNC:
602: printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
603: ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
604: break;
605: #endif
606: /*
607: * here follow the standard ioctls (filio.h etc.)
608: */
609: case FIONREAD: /* get # bytes to read */
610: /* if (rdch && rdch->bufhard.dl)
611: while (chn_rdfeed(rdch) == 0);
612: */ *arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0;
613: break;
614:
615: case FIOASYNC: /*set/clear async i/o */
616: DEB( printf("FIOASYNC\n") ; )
617: break;
618:
619: case SNDCTL_DSP_NONBLOCK:
620: case FIONBIO: /* set/clear non-blocking i/o */
621: if (rdch)
622: rdch->flags &= ~CHN_F_NBIO;
623: if (wrch)
624: wrch->flags &= ~CHN_F_NBIO;
625: if (*arg_i) {
626: if (rdch)
627: rdch->flags |= CHN_F_NBIO;
628: if (wrch)
629: wrch->flags |= CHN_F_NBIO;
630: }
631: break;
632:
633: /*
634: * Finally, here is the linux-compatible ioctl interface
635: */
636: #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
637: case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
638: case SNDCTL_DSP_GETBLKSIZE:
639: if (wrch)
640: *arg_i = sndbuf_getblksz(wrch->bufsoft);
641: else if (rdch)
642: *arg_i = sndbuf_getblksz(rdch->bufsoft);
643: else
644: *arg_i = 0;
645: break ;
646:
647: case SNDCTL_DSP_SETBLKSIZE:
648: RANGE(*arg_i, 16, 65536);
649: if (wrch) {
650: CHN_LOCK(wrch);
651: chn_setblocksize(wrch, 2, *arg_i);
652: CHN_UNLOCK(wrch);
653: }
654: if (rdch) {
655: CHN_LOCK(rdch);
656: chn_setblocksize(rdch, 2, *arg_i);
657: CHN_UNLOCK(rdch);
658: }
659: break;
660:
661: case SNDCTL_DSP_RESET:
662: DEB(printf("dsp reset\n"));
663: if (wrch)
664: chn_abort(wrch);
665: if (rdch)
666: chn_abort(rdch);
667: break;
668:
669: case SNDCTL_DSP_SYNC:
670: DEB(printf("dsp sync\n"));
671: /* chn_sync may sleep */
672: if (wrch) {
673: CHN_LOCK(wrch);
674: chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
675: CHN_UNLOCK(wrch);
676: }
677: break;
678:
679: case SNDCTL_DSP_SPEED:
680: /* chn_setspeed may sleep */
681: tmp = 0;
682: if (wrch) {
683: CHN_LOCK(wrch);
684: ret = chn_setspeed(wrch, *arg_i);
685: tmp = wrch->speed;
686: CHN_UNLOCK(wrch);
687: }
688: if (rdch && ret == 0) {
689: CHN_LOCK(rdch);
690: ret = chn_setspeed(rdch, *arg_i);
691: if (tmp == 0)
692: tmp = rdch->speed;
693: CHN_UNLOCK(rdch);
694: }
695: *arg_i = tmp;
696: break;
697:
698: case SOUND_PCM_READ_RATE:
699: *arg_i = wrch? wrch->speed : rdch->speed;
700: break;
701:
702: case SNDCTL_DSP_STEREO:
703: tmp = -1;
704: *arg_i = (*arg_i)? AFMT_STEREO : 0;
705: if (wrch) {
706: CHN_LOCK(wrch);
707: ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
708: tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
709: CHN_UNLOCK(wrch);
710: }
711: if (rdch && ret == 0) {
712: CHN_LOCK(rdch);
713: ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
714: if (tmp == -1)
715: tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
716: CHN_UNLOCK(rdch);
717: }
718: *arg_i = tmp;
719: break;
720:
721: case SOUND_PCM_WRITE_CHANNELS:
722: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
723: if (*arg_i != 0) {
724: tmp = 0;
725: *arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
726: if (wrch) {
727: CHN_LOCK(wrch);
728: ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
729: tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
730: CHN_UNLOCK(wrch);
731: }
732: if (rdch && ret == 0) {
733: CHN_LOCK(rdch);
734: ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
735: if (tmp == 0)
736: tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
737: CHN_UNLOCK(rdch);
738: }
739: *arg_i = tmp;
740: } else {
741: *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
742: }
743: break;
744:
745: case SOUND_PCM_READ_CHANNELS:
746: *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
747: break;
748:
749: case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
750: *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch);
751: break ;
752:
753: case SNDCTL_DSP_SETFMT: /* sets _one_ format */
754: /* XXX locking */
755: if ((*arg_i != AFMT_QUERY)) {
756: tmp = 0;
757: if (wrch) {
758: CHN_LOCK(wrch);
759: ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
760: tmp = wrch->format & ~AFMT_STEREO;
761: CHN_UNLOCK(wrch);
762: }
763: if (rdch && ret == 0) {
764: CHN_LOCK(rdch);
765: ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
766: if (tmp == 0)
767: tmp = rdch->format & ~AFMT_STEREO;
768: CHN_UNLOCK(rdch);
769: }
770: *arg_i = tmp;
771: } else
772: *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
773: break;
774:
775: case SNDCTL_DSP_SETFRAGMENT:
776: /* XXX locking */
777: DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
778: {
779: u_int32_t fragln = (*arg_i) & 0x0000ffff;
780: u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
781: u_int32_t fragsz;
782:
783: RANGE(fragln, 4, 16);
784: fragsz = 1 << fragln;
785:
786: if (maxfrags == 0)
787: maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
788: if (maxfrags < 2) {
789: ret = EINVAL;
790: break;
791: }
792: if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
793: maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
794:
795: DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
796: if (rdch) {
797: CHN_LOCK(rdch);
798: ret = chn_setblocksize(rdch, maxfrags, fragsz);
799: maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
800: fragsz = sndbuf_getblksz(rdch->bufsoft);
801: CHN_UNLOCK(rdch);
802: }
803: if (wrch && ret == 0) {
804: CHN_LOCK(wrch);
805: ret = chn_setblocksize(wrch, maxfrags, fragsz);
806: maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
807: fragsz = sndbuf_getblksz(wrch->bufsoft);
808: CHN_UNLOCK(wrch);
809: }
810:
811: fragln = 0;
812: while (fragsz > 1) {
813: fragln++;
814: fragsz >>= 1;
815: }
816: *arg_i = (maxfrags << 16) | fragln;
817: }
818: break;
819:
820: case SNDCTL_DSP_GETISPACE:
821: /* return the size of data available in the input queue */
822: {
823: audio_buf_info *a = (audio_buf_info *)arg;
824: if (rdch) {
825: struct snd_dbuf *bs = rdch->bufsoft;
826:
827: CHN_LOCK(rdch);
828: a->bytes = sndbuf_getready(bs);
829: a->fragments = a->bytes / sndbuf_getblksz(bs);
830: a->fragstotal = sndbuf_getblkcnt(bs);
831: a->fragsize = sndbuf_getblksz(bs);
832: CHN_UNLOCK(rdch);
833: }
834: }
835: break;
836:
837: case SNDCTL_DSP_GETOSPACE:
838: /* return space available in the output queue */
839: {
840: audio_buf_info *a = (audio_buf_info *)arg;
841: if (wrch) {
842: struct snd_dbuf *bs = wrch->bufsoft;
843:
844: CHN_LOCK(wrch);
845: chn_wrupdate(wrch);
846: a->bytes = sndbuf_getfree(bs);
847: a->fragments = a->bytes / sndbuf_getblksz(bs);
848: a->fragstotal = sndbuf_getblkcnt(bs);
849: a->fragsize = sndbuf_getblksz(bs);
850: CHN_UNLOCK(wrch);
851: }
852: }
853: break;
854:
855: case SNDCTL_DSP_GETIPTR:
856: {
857: count_info *a = (count_info *)arg;
858: if (rdch) {
859: struct snd_dbuf *bs = rdch->bufsoft;
860:
861: CHN_LOCK(rdch);
862: chn_rdupdate(rdch);
863: a->bytes = sndbuf_gettotal(bs);
864: a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
865: a->ptr = sndbuf_getreadyptr(bs);
866: rdch->blocks = sndbuf_getblocks(bs);
867: CHN_UNLOCK(rdch);
868: } else
869: ret = EINVAL;
870: }
871: break;
872:
873: case SNDCTL_DSP_GETOPTR:
874: {
875: count_info *a = (count_info *)arg;
876: if (wrch) {
877: struct snd_dbuf *bs = wrch->bufsoft;
878:
879: CHN_LOCK(wrch);
880: chn_wrupdate(wrch);
881: a->bytes = sndbuf_gettotal(bs);
882: a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
883: a->ptr = sndbuf_getreadyptr(bs);
884: wrch->blocks = sndbuf_getblocks(bs);
885: CHN_UNLOCK(wrch);
886: } else
887: ret = EINVAL;
888: }
889: break;
890:
891: case SNDCTL_DSP_GETCAPS:
892: *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
893: if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
894: *arg_i |= DSP_CAP_DUPLEX;
895: break;
896:
897: case SOUND_PCM_READ_BITS:
898: *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
899: break;
900:
901: case SNDCTL_DSP_SETTRIGGER:
902: if (rdch) {
903: CHN_LOCK(rdch);
904: rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
905: if (*arg_i & PCM_ENABLE_INPUT)
906: chn_start(rdch, 1);
907: else
908: rdch->flags |= CHN_F_NOTRIGGER;
909: CHN_UNLOCK(rdch);
910: }
911: if (wrch) {
912: CHN_LOCK(wrch);
913: wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
914: if (*arg_i & PCM_ENABLE_OUTPUT)
915: chn_start(wrch, 1);
916: else
917: wrch->flags |= CHN_F_NOTRIGGER;
918: CHN_UNLOCK(wrch);
919: }
920: break;
921:
922: case SNDCTL_DSP_GETTRIGGER:
923: *arg_i = 0;
924: if (wrch && wrch->flags & CHN_F_TRIGGERED)
925: *arg_i |= PCM_ENABLE_OUTPUT;
926: if (rdch && rdch->flags & CHN_F_TRIGGERED)
927: *arg_i |= PCM_ENABLE_INPUT;
928: break;
929:
930: case SNDCTL_DSP_GETODELAY:
931: if (wrch) {
932: struct snd_dbuf *b = wrch->bufhard;
933: struct snd_dbuf *bs = wrch->bufsoft;
934:
935: CHN_LOCK(wrch);
936: chn_wrupdate(wrch);
937: *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
938: CHN_UNLOCK(wrch);
939: } else
940: ret = EINVAL;
941: break;
942:
943: case SNDCTL_DSP_POST:
944: if (wrch) {
945: CHN_LOCK(wrch);
946: wrch->flags &= ~CHN_F_NOTRIGGER;
947: chn_start(wrch, 1);
948: CHN_UNLOCK(wrch);
949: }
950: break;
951:
952: case SNDCTL_DSP_MAPINBUF:
953: case SNDCTL_DSP_MAPOUTBUF:
954: case SNDCTL_DSP_SETSYNCRO:
955: /* undocumented */
956:
957: case SNDCTL_DSP_SUBDIVIDE:
958: case SOUND_PCM_WRITE_FILTER:
959: case SOUND_PCM_READ_FILTER:
960: /* dunno what these do, don't sound important */
961: default:
962: DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
963: ret = EINVAL;
964: break;
965: }
966: relchns(i_dev, rdch, wrch, 0);
967: splx(s);
968: return ret;
969: }
970:
971: static int
972: dsp_poll(dev_t i_dev, int events, struct thread *td)
973: {
974: struct pcm_channel *wrch = NULL, *rdch = NULL;
975: intrmask_t s;
976: int ret, e;
977:
978: s = spltty();
979: ret = 0;
980: getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
981:
982: if (wrch) {
983: e = (events & (POLLOUT | POLLWRNORM));
984: if (e)
985: ret |= chn_poll(wrch, e, td->td_proc);
986: }
987: if (rdch) {
988: e = (events & (POLLIN | POLLRDNORM));
989: if (e)
990: ret |= chn_poll(rdch, e, td->td_proc);
991: }
992: relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
993:
994: splx(s);
995: return ret;
996: }
997:
998: static int
999: dsp_mmap(dev_t i_dev, vm_offset_t offset, int nprot)
1000: {
1001: struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
1002: intrmask_t s;
1003: int ret;
1004:
1005: if (nprot & PROT_EXEC)
1006: return -1;
1007:
1008: s = spltty();
1009: getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1010: #if 0
1011: /*
1012: * XXX the linux api uses the nprot to select read/write buffer
1013: * our vm system doesn't allow this, so force write buffer
1014: */
1015:
1016: if (wrch && (nprot & PROT_WRITE)) {
1017: c = wrch;
1018: } else if (rdch && (nprot & PROT_READ)) {
1019: c = rdch;
1020: } else {
1021: splx(s);
1022: return -1;
1023: }
1024: #else
1025: c = wrch;
1026: #endif
1027:
1028: if (c == NULL) {
1029: relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1030: splx(s);
1031: return -1;
1032: }
1033:
1034: if (offset >= sndbuf_getsize(c->bufsoft)) {
1035: relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1036: splx(s);
1037: return -1;
1038: }
1039:
1040: if (!(c->flags & CHN_F_MAPPED))
1041: c->flags |= CHN_F_MAPPED;
1042:
1043: ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset)));
1044: relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
1045:
1046: splx(s);
1047: return ret;
1048: }
1049:
1050: int
1051: dsp_register(int unit, int channel)
1052: {
1053: cdevsw_add(&dsp_cdevsw,
1054: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0));
1055: cdevsw_add(&dsp_cdevsw,
1056: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0));
1057: cdevsw_add(&dsp_cdevsw,
1058: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
1059: cdevsw_add(&dsp_cdevsw,
1060: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0));
1061: make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel),
1062: UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
1063: make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
1064: UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
1065: make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
1066: UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
1067:
1068: return 0;
1069: }
1070:
1071: int
1072: dsp_registerrec(int unit, int channel)
1073: {
1074: cdevsw_add(&dsp_cdevsw,
1075: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0));
1076: make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSPREC, channel),
1077: UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel);
1078:
1079: return 0;
1080: }
1081:
1082: int
1083: dsp_unregister(int unit, int channel)
1084: {
1085: cdevsw_remove(&dsp_cdevsw,
1086: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0));
1087: cdevsw_remove(&dsp_cdevsw,
1088: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0));
1089: cdevsw_remove(&dsp_cdevsw,
1090: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
1091: cdevsw_remove(&dsp_cdevsw,
1092: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0));
1093: return 0;
1094: }
1095:
1096: int
1097: dsp_unregisterrec(int unit, int channel)
1098: {
1099: cdevsw_remove(&dsp_cdevsw,
1100: PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0));
1101: return 0;
1102: }
1103:
1104: #ifdef USING_DEVFS
1105: static void
1106: dsp_clone(void *arg, char *name, int namelen, dev_t *dev)
1107: {
1108: dev_t pdev;
1109: int i, cont, unit, devtype;
1110: int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
1111: char *devnames[3] = {"dsp", "dspW", "audio"};
1112:
1113: if (*dev != NODEV)
1114: return;
1115: if (pcm_devclass == NULL)
1116: return;
1117:
1118: devtype = 0;
1119: unit = -1;
1120: for (i = 0; (i < 3) && (unit == -1); i++) {
1121: devtype = devtypes[i];
1122: if (strcmp(name, devnames[i]) == 0) {
1123: unit = snd_unit;
1124: } else {
1125: if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
1126: unit = -1;
1127: }
1128: }
1129: if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
1130: return;
1131:
1132: cont = 1;
1133: for (i = 0; cont; i++) {
1134: pdev = make_adhoc_dev(&dsp_cdevsw, PCMMKMINOR(unit, devtype, i));
1135: if (pdev->si_flags & SI_NAMED) {
1136: if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
1137: *dev = pdev;
1138: return;
1139: }
1140: } else {
1141: cont = 0;
1142: }
1143: }
1144: }
1145:
1146: static void
1147: dsp_sysinit(void *p)
1148: {
1149: dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
1150: }
1151:
1152: static void
1153: dsp_sysuninit(void *p)
1154: {
1155: if (dsp_ehtag != NULL)
1156: EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
1157: }
1158:
1159: SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
1160: SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
1161: #endif
1162:
1163: