Annotation of src/lib/libossaudio/ossaudio.c, Revision 1.27
1.27 ! christos 1: /* $NetBSD: ossaudio.c,v 1.26 2011/09/13 19:10:18 christos Exp $ */
1.1 augustss 2:
1.7 augustss 3: /*-
1.1 augustss 4: * Copyright (c) 1997 The NetBSD Foundation, Inc.
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26: * POSSIBILITY OF SUCH DAMAGE.
27: */
1.18 lukem 28:
29: #include <sys/cdefs.h>
1.27 ! christos 30: __RCSID("$NetBSD: ossaudio.c,v 1.26 2011/09/13 19:10:18 christos Exp $");
1.1 augustss 31:
32: /*
33: * This is an OSS (Linux) sound API emulator.
34: * It provides the essentials of the API.
35: */
36:
37: /* XXX This file is essentially the same as sys/compat/ossaudio.c.
1.3 augustss 38: * With some preprocessor magic it could be the same file.
1.1 augustss 39: */
40:
41: #include <string.h>
42: #include <sys/types.h>
43: #include <sys/ioctl.h>
44: #include <sys/audioio.h>
45: #include <sys/stat.h>
46: #include <errno.h>
1.27 ! christos 47: #include <stdarg.h>
1.1 augustss 48:
49: #include "soundcard.h"
50: #undef ioctl
51:
52: #define GET_DEV(com) ((com) & 0xff)
53:
1.12 tron 54: #define TO_OSSVOL(x) (((x) * 100 + 127) / 255)
55: #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
1.1 augustss 56:
57: static struct audiodevinfo *getdevinfo(int);
58:
59: static void setblocksize(int, struct audio_info *);
60:
61: static int audio_ioctl(int, unsigned long, void *);
62: static int mixer_ioctl(int, unsigned long, void *);
1.27 ! christos 63: static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int);
! 64: static int enum_to_ord(struct audiodevinfo *, int);
! 65: static int enum_to_mask(struct audiodevinfo *, int);
1.1 augustss 66:
67: #define INTARG (*(int*)argp)
68:
69: int
1.27 ! christos 70: _oss_ioctl(int fd, unsigned long com, ...)
1.1 augustss 71: {
1.27 ! christos 72: va_list ap;
! 73: void *argp;
! 74:
! 75: va_start(ap, com);
! 76: argp = va_arg(ap, void *);
! 77: va_end(ap);
! 78:
1.1 augustss 79: if (IOCGROUP(com) == 'P')
80: return audio_ioctl(fd, com, argp);
81: else if (IOCGROUP(com) == 'M')
82: return mixer_ioctl(fd, com, argp);
83: else
84: return ioctl(fd, com, argp);
85: }
86:
87: static int
88: audio_ioctl(int fd, unsigned long com, void *argp)
89: {
1.8 simonb 90:
1.1 augustss 91: struct audio_info tmpinfo;
92: struct audio_offset tmpoffs;
93: struct audio_buf_info bufinfo;
94: struct count_info cntinfo;
95: struct audio_encoding tmpenc;
96: u_int u;
97: int idat, idata;
98: int retval;
99:
1.20 lukem 100: idat = 0;
101:
1.1 augustss 102: switch (com) {
103: case SNDCTL_DSP_RESET:
104: retval = ioctl(fd, AUDIO_FLUSH, 0);
105: if (retval < 0)
106: return retval;
107: break;
108: case SNDCTL_DSP_SYNC:
109: retval = ioctl(fd, AUDIO_DRAIN, 0);
110: if (retval < 0)
111: return retval;
1.16 mycroft 112: break;
113: case SNDCTL_DSP_POST:
114: /* This call is merely advisory, and may be a nop. */
1.1 augustss 115: break;
116: case SNDCTL_DSP_SPEED:
117: AUDIO_INITINFO(&tmpinfo);
118: tmpinfo.play.sample_rate =
119: tmpinfo.record.sample_rate = INTARG;
120: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
1.10 augustss 121: /* FALLTHRU */
1.1 augustss 122: case SOUND_PCM_READ_RATE:
1.21 joerg 123: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 124: if (retval < 0)
125: return retval;
126: INTARG = tmpinfo.play.sample_rate;
127: break;
128: case SNDCTL_DSP_STEREO:
129: AUDIO_INITINFO(&tmpinfo);
130: tmpinfo.play.channels =
131: tmpinfo.record.channels = INTARG ? 2 : 1;
132: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
1.21 joerg 133: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 134: if (retval < 0)
135: return retval;
136: INTARG = tmpinfo.play.channels - 1;
137: break;
138: case SNDCTL_DSP_GETBLKSIZE:
1.21 joerg 139: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 140: if (retval < 0)
141: return retval;
142: setblocksize(fd, &tmpinfo);
143: INTARG = tmpinfo.blocksize;
144: break;
145: case SNDCTL_DSP_SETFMT:
1.4 augustss 146: AUDIO_INITINFO(&tmpinfo);
1.1 augustss 147: switch (INTARG) {
148: case AFMT_MU_LAW:
149: tmpinfo.play.precision =
150: tmpinfo.record.precision = 8;
151: tmpinfo.play.encoding =
152: tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
153: break;
154: case AFMT_A_LAW:
155: tmpinfo.play.precision =
156: tmpinfo.record.precision = 8;
157: tmpinfo.play.encoding =
158: tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
159: break;
160: case AFMT_U8:
161: tmpinfo.play.precision =
162: tmpinfo.record.precision = 8;
163: tmpinfo.play.encoding =
164: tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
165: break;
166: case AFMT_S8:
167: tmpinfo.play.precision =
168: tmpinfo.record.precision = 8;
169: tmpinfo.play.encoding =
170: tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR;
171: break;
172: case AFMT_S16_LE:
173: tmpinfo.play.precision =
174: tmpinfo.record.precision = 16;
175: tmpinfo.play.encoding =
176: tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
177: break;
178: case AFMT_S16_BE:
179: tmpinfo.play.precision =
180: tmpinfo.record.precision = 16;
181: tmpinfo.play.encoding =
182: tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
183: break;
184: case AFMT_U16_LE:
185: tmpinfo.play.precision =
186: tmpinfo.record.precision = 16;
187: tmpinfo.play.encoding =
188: tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
189: break;
190: case AFMT_U16_BE:
191: tmpinfo.play.precision =
192: tmpinfo.record.precision = 16;
193: tmpinfo.play.encoding =
194: tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
195: break;
1.25 jmcneill 196: case AFMT_AC3:
197: tmpinfo.play.precision =
198: tmpinfo.record.precision = 16;
199: tmpinfo.play.encoding =
200: tmpinfo.record.encoding = AUDIO_ENCODING_AC3;
201: break;
1.1 augustss 202: default:
203: return EINVAL;
204: }
205: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
1.10 augustss 206: /* FALLTHRU */
1.1 augustss 207: case SOUND_PCM_READ_BITS:
1.21 joerg 208: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 209: if (retval < 0)
210: return retval;
211: switch (tmpinfo.play.encoding) {
212: case AUDIO_ENCODING_ULAW:
213: idat = AFMT_MU_LAW;
214: break;
215: case AUDIO_ENCODING_ALAW:
216: idat = AFMT_A_LAW;
217: break;
218: case AUDIO_ENCODING_SLINEAR_LE:
219: if (tmpinfo.play.precision == 16)
220: idat = AFMT_S16_LE;
221: else
222: idat = AFMT_S8;
223: break;
224: case AUDIO_ENCODING_SLINEAR_BE:
225: if (tmpinfo.play.precision == 16)
226: idat = AFMT_S16_BE;
227: else
228: idat = AFMT_S8;
229: break;
230: case AUDIO_ENCODING_ULINEAR_LE:
231: if (tmpinfo.play.precision == 16)
232: idat = AFMT_U16_LE;
233: else
234: idat = AFMT_U8;
235: break;
236: case AUDIO_ENCODING_ULINEAR_BE:
237: if (tmpinfo.play.precision == 16)
238: idat = AFMT_U16_BE;
239: else
240: idat = AFMT_U8;
241: break;
242: case AUDIO_ENCODING_ADPCM:
243: idat = AFMT_IMA_ADPCM;
244: break;
1.25 jmcneill 245: case AUDIO_ENCODING_AC3:
246: idat = AFMT_AC3;
247: break;
1.1 augustss 248: }
249: INTARG = idat;
250: break;
251: case SNDCTL_DSP_CHANNELS:
252: AUDIO_INITINFO(&tmpinfo);
1.23 drochner 253: tmpinfo.play.channels = INTARG;
254: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
255: AUDIO_INITINFO(&tmpinfo);
1.1 augustss 256: tmpinfo.record.channels = INTARG;
257: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
1.10 augustss 258: /* FALLTHRU */
1.1 augustss 259: case SOUND_PCM_READ_CHANNELS:
1.21 joerg 260: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 261: if (retval < 0)
262: return retval;
263: INTARG = tmpinfo.play.channels;
264: break;
265: case SOUND_PCM_WRITE_FILTER:
266: case SOUND_PCM_READ_FILTER:
267: errno = EINVAL;
268: return -1; /* XXX unimplemented */
269: case SNDCTL_DSP_SUBDIVIDE:
1.21 joerg 270: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 271: if (retval < 0)
272: return retval;
273: setblocksize(fd, &tmpinfo);
274: idat = INTARG;
275: if (idat == 0)
1.2 augustss 276: idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
277: idat = (tmpinfo.play.buffer_size / idat) & -4;
1.1 augustss 278: AUDIO_INITINFO(&tmpinfo);
279: tmpinfo.blocksize = idat;
280: retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
281: if (retval < 0)
282: return retval;
1.2 augustss 283: INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize;
1.1 augustss 284: break;
285: case SNDCTL_DSP_SETFRAGMENT:
286: AUDIO_INITINFO(&tmpinfo);
287: idat = INTARG;
288: if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17)
289: return EINVAL;
290: tmpinfo.blocksize = 1 << (idat & 0xffff);
1.11 augustss 291: tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff;
1.1 augustss 292: if (tmpinfo.hiwat == 0) /* 0 means set to max */
293: tmpinfo.hiwat = 65536;
294: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
1.21 joerg 295: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 296: if (retval < 0)
297: return retval;
298: u = tmpinfo.blocksize;
1.6 augustss 299: for(idat = 0; u > 1; idat++, u >>= 1)
1.1 augustss 300: ;
1.5 augustss 301: idat |= (tmpinfo.hiwat & 0x7fff) << 16;
1.1 augustss 302: INTARG = idat;
303: break;
304: case SNDCTL_DSP_GETFMTS:
1.8 simonb 305: for(idat = 0, tmpenc.index = 0;
306: ioctl(fd, AUDIO_GETENC, &tmpenc) == 0;
1.1 augustss 307: tmpenc.index++) {
308: switch(tmpenc.encoding) {
309: case AUDIO_ENCODING_ULAW:
310: idat |= AFMT_MU_LAW;
311: break;
312: case AUDIO_ENCODING_ALAW:
313: idat |= AFMT_A_LAW;
314: break;
315: case AUDIO_ENCODING_SLINEAR:
316: idat |= AFMT_S8;
317: break;
318: case AUDIO_ENCODING_SLINEAR_LE:
319: if (tmpenc.precision == 16)
320: idat |= AFMT_S16_LE;
321: else
322: idat |= AFMT_S8;
323: break;
324: case AUDIO_ENCODING_SLINEAR_BE:
325: if (tmpenc.precision == 16)
326: idat |= AFMT_S16_BE;
327: else
328: idat |= AFMT_S8;
329: break;
330: case AUDIO_ENCODING_ULINEAR:
331: idat |= AFMT_U8;
332: break;
333: case AUDIO_ENCODING_ULINEAR_LE:
334: if (tmpenc.precision == 16)
335: idat |= AFMT_U16_LE;
336: else
337: idat |= AFMT_U8;
338: break;
339: case AUDIO_ENCODING_ULINEAR_BE:
340: if (tmpenc.precision == 16)
341: idat |= AFMT_U16_BE;
342: else
343: idat |= AFMT_U8;
344: break;
345: case AUDIO_ENCODING_ADPCM:
346: idat |= AFMT_IMA_ADPCM;
347: break;
1.25 jmcneill 348: case AUDIO_ENCODING_AC3:
349: idat |= AFMT_AC3;
350: break;
1.1 augustss 351: default:
352: break;
353: }
354: }
355: INTARG = idat;
356: break;
357: case SNDCTL_DSP_GETOSPACE:
1.21 joerg 358: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.14 augustss 359: if (retval < 0)
360: return retval;
361: setblocksize(fd, &tmpinfo);
362: bufinfo.fragsize = tmpinfo.blocksize;
1.27 ! christos 363: bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek
! 364: + tmpinfo.blocksize - 1) / tmpinfo.blocksize;
1.14 augustss 365: bufinfo.fragstotal = tmpinfo.hiwat;
1.27 ! christos 366: bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize
! 367: - tmpinfo.play.seek;
1.14 augustss 368: *(struct audio_buf_info *)argp = bufinfo;
369: break;
1.1 augustss 370: case SNDCTL_DSP_GETISPACE:
1.21 joerg 371: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 372: if (retval < 0)
373: return retval;
374: setblocksize(fd, &tmpinfo);
375: bufinfo.fragsize = tmpinfo.blocksize;
1.27 ! christos 376: bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.record.seek +
! 377: tmpinfo.blocksize - 1) / tmpinfo.blocksize;
1.13 augustss 378: bufinfo.fragstotal = tmpinfo.hiwat;
1.27 ! christos 379: bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize
! 380: - tmpinfo.record.seek;
1.1 augustss 381: *(struct audio_buf_info *)argp = bufinfo;
382: break;
383: case SNDCTL_DSP_NONBLOCK:
384: idat = 1;
385: retval = ioctl(fd, FIONBIO, &idat);
386: if (retval < 0)
387: return retval;
388: break;
389: case SNDCTL_DSP_GETCAPS:
1.10 augustss 390: retval = ioctl(fd, AUDIO_GETPROPS, &idata);
1.1 augustss 391: if (retval < 0)
392: return retval;
393: idat = DSP_CAP_TRIGGER; /* pretend we have trigger */
394: if (idata & AUDIO_PROP_FULLDUPLEX)
395: idat |= DSP_CAP_DUPLEX;
396: if (idata & AUDIO_PROP_MMAP)
397: idat |= DSP_CAP_MMAP;
398: INTARG = idat;
399: break;
400: #if 0
401: case SNDCTL_DSP_GETTRIGGER:
1.21 joerg 402: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
1.1 augustss 403: if (retval < 0)
404: return retval;
405: idat = (tmpinfo.play.pause ? 0 : PCM_ENABLE_OUTPUT) |
406: (tmpinfo.record.pause ? 0 : PCM_ENABLE_INPUT);
407: retval = copyout(&idat, SCARG(uap, data), sizeof idat);
408: if (retval < 0)
409: return retval;
410: break;
411: case SNDCTL_DSP_SETTRIGGER:
412: AUDIO_INITINFO(&tmpinfo);
413: retval = copyin(SCARG(uap, data), &idat, sizeof idat);
414: if (retval < 0)
415: return retval;
416: tmpinfo.play.pause = (idat & PCM_ENABLE_OUTPUT) == 0;
417: tmpinfo.record.pause = (idat & PCM_ENABLE_INPUT) == 0;
1.10 augustss 418: (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
1.1 augustss 419: retval = copyout(&idat, SCARG(uap, data), sizeof idat);
420: if (retval < 0)
421: return retval;
422: break;
423: #else
424: case SNDCTL_DSP_GETTRIGGER:
425: case SNDCTL_DSP_SETTRIGGER:
426: /* XXX Do nothing for now. */
427: INTARG = PCM_ENABLE_OUTPUT;
428: break;
429: #endif
430: case SNDCTL_DSP_GETIPTR:
431: retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs);
432: if (retval < 0)
433: return retval;
434: cntinfo.bytes = tmpoffs.samples;
435: cntinfo.blocks = tmpoffs.deltablks;
436: cntinfo.ptr = tmpoffs.offset;
437: *(struct count_info *)argp = cntinfo;
438: break;
439: case SNDCTL_DSP_GETOPTR:
440: retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs);
441: if (retval < 0)
442: return retval;
443: cntinfo.bytes = tmpoffs.samples;
444: cntinfo.blocks = tmpoffs.deltablks;
445: cntinfo.ptr = tmpoffs.offset;
446: *(struct count_info *)argp = cntinfo;
447: break;
1.17 jdolecek 448: case SNDCTL_DSP_SETDUPLEX:
449: idat = 1;
450: retval = ioctl(fd, AUDIO_SETFD, &idat);
451: if (retval < 0)
452: return retval;
453: break;
1.22 mlelstv 454: case SNDCTL_DSP_GETODELAY:
455: retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
456: if (retval < 0)
457: return retval;
458: idat = tmpinfo.play.seek + tmpinfo.blocksize / 2;
459: INTARG = idat;
460: break;
461: case SNDCTL_DSP_PROFILE:
462: /* This gives just a hint to the driver,
463: * implementing it as a NOP is ok
464: */
465: break;
1.1 augustss 466: case SNDCTL_DSP_MAPINBUF:
467: case SNDCTL_DSP_MAPOUTBUF:
468: case SNDCTL_DSP_SETSYNCRO:
469: errno = EINVAL;
470: return -1; /* XXX unimplemented */
471: default:
472: errno = EINVAL;
473: return -1;
474: }
475:
476: return 0;
477: }
478:
479:
1.11 augustss 480: /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices
1.1 augustss 481: * some will not be available to Linux */
1.11 augustss 482: #define NETBSD_MAXDEVS 64
1.1 augustss 483: struct audiodevinfo {
484: int done;
485: dev_t dev;
1.8 simonb 486: int16_t devmap[SOUND_MIXER_NRDEVICES],
1.1 augustss 487: rdevmap[NETBSD_MAXDEVS];
1.11 augustss 488: char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN];
489: int enum2opaque[NETBSD_MAXDEVS];
1.1 augustss 490: u_long devmask, recmask, stereomask;
1.20 lukem 491: u_long caps;
492: int source;
1.1 augustss 493: };
494:
1.11 augustss 495: static int
496: opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq)
497: {
498: int i, o;
499:
500: for (i = 0; i < NETBSD_MAXDEVS; i++) {
501: o = di->enum2opaque[i];
502: if (o == opq)
503: break;
504: if (o == -1 && label != NULL &&
505: !strncmp(di->names[i], label->name, sizeof di->names[i])) {
506: di->enum2opaque[i] = opq;
507: break;
508: }
509: }
510: if (i >= NETBSD_MAXDEVS)
511: i = -1;
512: /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/
513: return (i);
514: }
515:
516: static int
517: enum_to_ord(struct audiodevinfo *di, int enm)
518: {
519: if (enm >= NETBSD_MAXDEVS)
520: return (-1);
521:
522: /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/
523: return (di->enum2opaque[enm]);
524: }
525:
526: static int
527: enum_to_mask(struct audiodevinfo *di, int enm)
528: {
529: int m;
530: if (enm >= NETBSD_MAXDEVS)
531: return (0);
532:
533: m = di->enum2opaque[enm];
534: if (m == -1)
535: m = 0;
536: /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/
537: return (m);
538: }
539:
1.8 simonb 540: /*
1.1 augustss 541: * Collect the audio device information to allow faster
542: * emulation of the Linux mixer ioctls. Cache the information
543: * to eliminate the overhead of repeating all the ioctls needed
544: * to collect the information.
545: */
546: static struct audiodevinfo *
547: getdevinfo(int fd)
548: {
549: mixer_devinfo_t mi;
1.11 augustss 550: int i, j, e;
1.1 augustss 551: static struct {
552: char *name;
553: int code;
554: } *dp, devs[] = {
555: { AudioNmicrophone, SOUND_MIXER_MIC },
556: { AudioNline, SOUND_MIXER_LINE },
557: { AudioNcd, SOUND_MIXER_CD },
558: { AudioNdac, SOUND_MIXER_PCM },
1.15 kim 559: { AudioNaux, SOUND_MIXER_LINE1 },
1.1 augustss 560: { AudioNrecord, SOUND_MIXER_IMIX },
561: { AudioNmaster, SOUND_MIXER_VOLUME },
562: { AudioNtreble, SOUND_MIXER_TREBLE },
563: { AudioNbass, SOUND_MIXER_BASS },
564: { AudioNspeaker, SOUND_MIXER_SPEAKER },
565: /* { AudioNheadphone, ?? },*/
566: { AudioNoutput, SOUND_MIXER_OGAIN },
567: { AudioNinput, SOUND_MIXER_IGAIN },
568: /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/
569: /* { AudioNstereo, ?? },*/
570: /* { AudioNmono, ?? },*/
571: { AudioNfmsynth, SOUND_MIXER_SYNTH },
572: /* { AudioNwave, SOUND_MIXER_PCM },*/
573: { AudioNmidi, SOUND_MIXER_SYNTH },
574: /* { AudioNmixerout, ?? },*/
575: { 0, -1 }
576: };
577: static struct audiodevinfo devcache = { 0 };
578: struct audiodevinfo *di = &devcache;
579: struct stat sb;
1.27 ! christos 580: size_t mlen, dlen;
1.1 augustss 581:
582: /* Figure out what device it is so we can check if the
583: * cached data is valid.
584: */
585: if (fstat(fd, &sb) < 0)
586: return 0;
587: if (di->done && di->dev == sb.st_dev)
588: return di;
589:
590: di->done = 1;
591: di->dev = sb.st_dev;
592: di->devmask = 0;
593: di->recmask = 0;
594: di->stereomask = 0;
1.11 augustss 595: di->source = ~0;
1.1 augustss 596: di->caps = 0;
597: for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
598: di->devmap[i] = -1;
1.11 augustss 599: for(i = 0; i < NETBSD_MAXDEVS; i++) {
1.1 augustss 600: di->rdevmap[i] = -1;
1.11 augustss 601: di->names[i][0] = '\0';
602: di->enum2opaque[i] = -1;
603: }
1.1 augustss 604: for(i = 0; i < NETBSD_MAXDEVS; i++) {
605: mi.index = i;
606: if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
607: break;
608: switch(mi.type) {
609: case AUDIO_MIXER_VALUE:
1.19 kent 610: for(dp = devs; dp->name; dp++) {
611: if (strcmp(dp->name, mi.label.name) == 0)
1.1 augustss 612: break;
1.19 kent 613: dlen = strlen(dp->name);
614: mlen = strlen(mi.label.name);
615: if (dlen < mlen
616: && mi.label.name[mlen-dlen-1] == '.'
1.27 ! christos 617: && strcmp(dp->name,
! 618: mi.label.name + mlen - dlen) == 0)
1.19 kent 619: break;
620: }
1.1 augustss 621: if (dp->code >= 0) {
622: di->devmap[dp->code] = i;
623: di->rdevmap[i] = dp->code;
624: di->devmask |= 1 << dp->code;
625: if (mi.un.v.num_channels == 2)
626: di->stereomask |= 1 << dp->code;
1.11 augustss 627: strncpy(di->names[i], mi.label.name,
628: sizeof di->names[i]);
1.1 augustss 629: }
630: break;
1.11 augustss 631: }
632: }
633: for(i = 0; i < NETBSD_MAXDEVS; i++) {
634: mi.index = i;
635: if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
636: break;
637: if (strcmp(mi.label.name, AudioNsource) != 0)
638: continue;
639: di->source = i;
640: switch(mi.type) {
1.1 augustss 641: case AUDIO_MIXER_ENUM:
1.11 augustss 642: for(j = 0; j < mi.un.e.num_mem; j++) {
643: e = opaque_to_enum(di,
644: &mi.un.e.member[j].label,
645: mi.un.e.member[j].ord);
646: if (e >= 0)
647: di->recmask |= 1 << di->rdevmap[e];
1.1 augustss 648: }
1.11 augustss 649: di->caps = SOUND_CAP_EXCL_INPUT;
1.1 augustss 650: break;
651: case AUDIO_MIXER_SET:
1.11 augustss 652: for(j = 0; j < mi.un.s.num_mem; j++) {
653: e = opaque_to_enum(di,
654: &mi.un.s.member[j].label,
655: mi.un.s.member[j].mask);
656: if (e >= 0)
657: di->recmask |= 1 << di->rdevmap[e];
1.1 augustss 658: }
659: break;
660: }
661: }
662: return di;
663: }
664:
665: int
666: mixer_ioctl(int fd, unsigned long com, void *argp)
1.8 simonb 667: {
1.1 augustss 668: struct audiodevinfo *di;
1.10 augustss 669: struct mixer_info *omi;
670: struct audio_device adev;
1.1 augustss 671: mixer_ctrl_t mc;
1.27 ! christos 672: u_long idat, n;
1.1 augustss 673: int i;
674: int retval;
1.27 ! christos 675: int l, r, error, e;
1.1 augustss 676:
1.20 lukem 677: idat = 0;
1.1 augustss 678: di = getdevinfo(fd);
679: if (di == 0)
680: return -1;
681:
682: switch (com) {
1.11 augustss 683: case OSS_GETVERSION:
684: idat = SOUND_VERSION;
685: break;
1.10 augustss 686: case SOUND_MIXER_INFO:
687: case SOUND_OLD_MIXER_INFO:
688: error = ioctl(fd, AUDIO_GETDEV, &adev);
689: if (error)
690: return (error);
691: omi = argp;
692: if (com == SOUND_MIXER_INFO)
693: omi->modify_counter = 1;
694: strncpy(omi->id, adev.name, sizeof omi->id);
695: strncpy(omi->name, adev.name, sizeof omi->name);
696: return 0;
1.1 augustss 697: case SOUND_MIXER_READ_RECSRC:
698: if (di->source == -1)
699: return EINVAL;
700: mc.dev = di->source;
701: if (di->caps & SOUND_CAP_EXCL_INPUT) {
702: mc.type = AUDIO_MIXER_ENUM;
703: retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
704: if (retval < 0)
705: return retval;
1.11 augustss 706: e = opaque_to_enum(di, NULL, mc.un.ord);
707: if (e >= 0)
708: idat = 1 << di->rdevmap[e];
1.1 augustss 709: } else {
710: mc.type = AUDIO_MIXER_SET;
711: retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
712: if (retval < 0)
713: return retval;
1.11 augustss 714: e = opaque_to_enum(di, NULL, mc.un.mask);
715: if (e >= 0)
716: idat = 1 << di->rdevmap[e];
1.1 augustss 717: }
718: break;
719: case SOUND_MIXER_READ_DEVMASK:
720: idat = di->devmask;
721: break;
722: case SOUND_MIXER_READ_RECMASK:
723: idat = di->recmask;
724: break;
725: case SOUND_MIXER_READ_STEREODEVS:
726: idat = di->stereomask;
727: break;
728: case SOUND_MIXER_READ_CAPS:
729: idat = di->caps;
730: break;
731: case SOUND_MIXER_WRITE_RECSRC:
732: case SOUND_MIXER_WRITE_R_RECSRC:
733: if (di->source == -1)
734: return EINVAL;
735: mc.dev = di->source;
736: idat = INTARG;
737: if (di->caps & SOUND_CAP_EXCL_INPUT) {
738: mc.type = AUDIO_MIXER_ENUM;
739: for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
740: if (idat & (1 << i))
741: break;
742: if (i >= SOUND_MIXER_NRDEVICES ||
743: di->devmap[i] == -1)
744: return EINVAL;
1.11 augustss 745: mc.un.ord = enum_to_ord(di, di->devmap[i]);
1.1 augustss 746: } else {
747: mc.type = AUDIO_MIXER_SET;
748: mc.un.mask = 0;
749: for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
750: if (idat & (1 << i)) {
751: if (di->devmap[i] == -1)
752: return EINVAL;
1.27 ! christos 753: mc.un.mask |=
! 754: enum_to_mask(di, di->devmap[i]);
1.1 augustss 755: }
756: }
757: }
758: return ioctl(fd, AUDIO_MIXER_WRITE, &mc);
759: default:
760: if (MIXER_READ(SOUND_MIXER_FIRST) <= com &&
761: com < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
762: n = GET_DEV(com);
763: if (di->devmap[n] == -1)
764: return EINVAL;
765: mc.dev = di->devmap[n];
766: mc.type = AUDIO_MIXER_VALUE;
767: doread:
1.27 ! christos 768: mc.un.value.num_channels =
! 769: di->stereomask & (1 << (u_int)n) ? 2 : 1;
1.1 augustss 770: retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
771: if (retval < 0)
772: return retval;
773: if (mc.type != AUDIO_MIXER_VALUE)
774: return EINVAL;
775: if (mc.un.value.num_channels != 2) {
1.27 ! christos 776: l = r =
! 777: mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
1.1 augustss 778: } else {
779: l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
780: r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
781: }
782: idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
783: break;
784: } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com &&
785: com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) ||
786: (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
787: com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) {
788: n = GET_DEV(com);
789: if (di->devmap[n] == -1)
790: return EINVAL;
791: idat = INTARG;
1.27 ! christos 792: l = FROM_OSSVOL((u_int)idat & 0xff);
1.26 christos 793: r = FROM_OSSVOL(((u_int)idat >> 8) & 0xff);
1.1 augustss 794: mc.dev = di->devmap[n];
795: mc.type = AUDIO_MIXER_VALUE;
1.27 ! christos 796: if (di->stereomask & (1 << (u_int)n)) {
1.1 augustss 797: mc.un.value.num_channels = 2;
798: mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
799: mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
800: } else {
801: mc.un.value.num_channels = 1;
1.27 ! christos 802: mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] =
! 803: (l + r) / 2;
1.1 augustss 804: }
805: retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc);
806: if (retval < 0)
807: return retval;
808: if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
809: com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))
810: return 0;
811: goto doread;
812: } else {
813: errno = EINVAL;
814: return -1;
815: }
816: }
1.27 ! christos 817: INTARG = (int)idat;
1.1 augustss 818: return 0;
819: }
820:
821: /*
822: * Check that the blocksize is a power of 2 as OSS wants.
823: * If not, set it to be.
824: */
1.8 simonb 825: static void
1.1 augustss 826: setblocksize(int fd, struct audio_info *info)
827: {
828: struct audio_info set;
829: int s;
830:
831: if (info->blocksize & (info->blocksize-1)) {
832: for(s = 32; s < info->blocksize; s <<= 1)
833: ;
834: AUDIO_INITINFO(&set);
835: set.blocksize = s;
836: ioctl(fd, AUDIO_SETINFO, &set);
1.21 joerg 837: ioctl(fd, AUDIO_GETBUFINFO, info);
1.1 augustss 838: }
839: }
CVSweb <webmaster@jp.NetBSD.org>