1 /* $NetBSD: ossaudio.c,v 1.23 2007/11/08 20:27:25 drochner Exp $ */
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: ossaudio.c,v 1.23 2007/11/08 20:27:25 drochner Exp $");
33 * This is an OSS (Linux) sound API emulator.
34 * It provides the essentials of the API.
37 /* XXX This file is essentially the same as sys/compat/ossaudio.c.
38 * With some preprocessor magic it could be the same file.
42 #include <sys/types.h>
43 #include <sys/ioctl.h>
44 #include <sys/audioio.h>
48 #include "soundcard.h"
51 #define GET_DEV(com) ((com) & 0xff)
53 #define TO_OSSVOL(x) (((x) * 100 + 127) / 255)
54 #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
56 static struct audiodevinfo
*getdevinfo(int);
58 static void setblocksize(int, struct audio_info
*);
60 static int audio_ioctl(int, unsigned long, void *);
61 static int mixer_ioctl(int, unsigned long, void *);
62 static int opaque_to_enum(struct audiodevinfo
*di
, audio_mixer_name_t
*label
, int opq
);
63 static int enum_to_ord(struct audiodevinfo
*di
, int enm
);
64 static int enum_to_mask(struct audiodevinfo
*di
, int enm
);
66 #define INTARG (*(int*)argp)
69 _oss_ioctl(int fd
, unsigned long com
, void *argp
)
71 if (IOCGROUP(com
) == 'P')
72 return audio_ioctl(fd
, com
, argp
);
73 else if (IOCGROUP(com
) == 'M')
74 return mixer_ioctl(fd
, com
, argp
);
76 return ioctl(fd
, com
, argp
);
80 audio_ioctl(int fd
, unsigned long com
, void *argp
)
83 struct audio_info tmpinfo
;
84 struct audio_offset tmpoffs
;
85 struct audio_buf_info bufinfo
;
86 struct count_info cntinfo
;
87 struct audio_encoding tmpenc
;
95 case SNDCTL_DSP_RESET
:
96 retval
= ioctl(fd
, AUDIO_FLUSH
, 0);
100 case SNDCTL_DSP_SYNC
:
101 retval
= ioctl(fd
, AUDIO_DRAIN
, 0);
105 case SNDCTL_DSP_POST
:
106 /* This call is merely advisory, and may be a nop. */
108 case SNDCTL_DSP_SPEED
:
109 AUDIO_INITINFO(&tmpinfo
);
110 tmpinfo
.play
.sample_rate
=
111 tmpinfo
.record
.sample_rate
= INTARG
;
112 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
114 case SOUND_PCM_READ_RATE
:
115 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
118 INTARG
= tmpinfo
.play
.sample_rate
;
120 case SNDCTL_DSP_STEREO
:
121 AUDIO_INITINFO(&tmpinfo
);
122 tmpinfo
.play
.channels
=
123 tmpinfo
.record
.channels
= INTARG
? 2 : 1;
124 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
125 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
128 INTARG
= tmpinfo
.play
.channels
- 1;
130 case SNDCTL_DSP_GETBLKSIZE
:
131 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
134 setblocksize(fd
, &tmpinfo
);
135 INTARG
= tmpinfo
.blocksize
;
137 case SNDCTL_DSP_SETFMT
:
138 AUDIO_INITINFO(&tmpinfo
);
141 tmpinfo
.play
.precision
=
142 tmpinfo
.record
.precision
= 8;
143 tmpinfo
.play
.encoding
=
144 tmpinfo
.record
.encoding
= AUDIO_ENCODING_ULAW
;
147 tmpinfo
.play
.precision
=
148 tmpinfo
.record
.precision
= 8;
149 tmpinfo
.play
.encoding
=
150 tmpinfo
.record
.encoding
= AUDIO_ENCODING_ALAW
;
153 tmpinfo
.play
.precision
=
154 tmpinfo
.record
.precision
= 8;
155 tmpinfo
.play
.encoding
=
156 tmpinfo
.record
.encoding
= AUDIO_ENCODING_ULINEAR
;
159 tmpinfo
.play
.precision
=
160 tmpinfo
.record
.precision
= 8;
161 tmpinfo
.play
.encoding
=
162 tmpinfo
.record
.encoding
= AUDIO_ENCODING_SLINEAR
;
165 tmpinfo
.play
.precision
=
166 tmpinfo
.record
.precision
= 16;
167 tmpinfo
.play
.encoding
=
168 tmpinfo
.record
.encoding
= AUDIO_ENCODING_SLINEAR_LE
;
171 tmpinfo
.play
.precision
=
172 tmpinfo
.record
.precision
= 16;
173 tmpinfo
.play
.encoding
=
174 tmpinfo
.record
.encoding
= AUDIO_ENCODING_SLINEAR_BE
;
177 tmpinfo
.play
.precision
=
178 tmpinfo
.record
.precision
= 16;
179 tmpinfo
.play
.encoding
=
180 tmpinfo
.record
.encoding
= AUDIO_ENCODING_ULINEAR_LE
;
183 tmpinfo
.play
.precision
=
184 tmpinfo
.record
.precision
= 16;
185 tmpinfo
.play
.encoding
=
186 tmpinfo
.record
.encoding
= AUDIO_ENCODING_ULINEAR_BE
;
191 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
193 case SOUND_PCM_READ_BITS
:
194 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
197 switch (tmpinfo
.play
.encoding
) {
198 case AUDIO_ENCODING_ULAW
:
201 case AUDIO_ENCODING_ALAW
:
204 case AUDIO_ENCODING_SLINEAR_LE
:
205 if (tmpinfo
.play
.precision
== 16)
210 case AUDIO_ENCODING_SLINEAR_BE
:
211 if (tmpinfo
.play
.precision
== 16)
216 case AUDIO_ENCODING_ULINEAR_LE
:
217 if (tmpinfo
.play
.precision
== 16)
222 case AUDIO_ENCODING_ULINEAR_BE
:
223 if (tmpinfo
.play
.precision
== 16)
228 case AUDIO_ENCODING_ADPCM
:
229 idat
= AFMT_IMA_ADPCM
;
234 case SNDCTL_DSP_CHANNELS
:
235 AUDIO_INITINFO(&tmpinfo
);
236 tmpinfo
.play
.channels
= INTARG
;
237 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
238 AUDIO_INITINFO(&tmpinfo
);
239 tmpinfo
.record
.channels
= INTARG
;
240 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
242 case SOUND_PCM_READ_CHANNELS
:
243 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
246 INTARG
= tmpinfo
.play
.channels
;
248 case SOUND_PCM_WRITE_FILTER
:
249 case SOUND_PCM_READ_FILTER
:
251 return -1; /* XXX unimplemented */
252 case SNDCTL_DSP_SUBDIVIDE
:
253 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
256 setblocksize(fd
, &tmpinfo
);
259 idat
= tmpinfo
.play
.buffer_size
/ tmpinfo
.blocksize
;
260 idat
= (tmpinfo
.play
.buffer_size
/ idat
) & -4;
261 AUDIO_INITINFO(&tmpinfo
);
262 tmpinfo
.blocksize
= idat
;
263 retval
= ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
266 INTARG
= tmpinfo
.play
.buffer_size
/ tmpinfo
.blocksize
;
268 case SNDCTL_DSP_SETFRAGMENT
:
269 AUDIO_INITINFO(&tmpinfo
);
271 if ((idat
& 0xffff) < 4 || (idat
& 0xffff) > 17)
273 tmpinfo
.blocksize
= 1 << (idat
& 0xffff);
274 tmpinfo
.hiwat
= ((unsigned)idat
>> 16) & 0x7fff;
275 if (tmpinfo
.hiwat
== 0) /* 0 means set to max */
276 tmpinfo
.hiwat
= 65536;
277 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
278 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
281 u
= tmpinfo
.blocksize
;
282 for(idat
= 0; u
> 1; idat
++, u
>>= 1)
284 idat
|= (tmpinfo
.hiwat
& 0x7fff) << 16;
287 case SNDCTL_DSP_GETFMTS
:
288 for(idat
= 0, tmpenc
.index
= 0;
289 ioctl(fd
, AUDIO_GETENC
, &tmpenc
) == 0;
291 switch(tmpenc
.encoding
) {
292 case AUDIO_ENCODING_ULAW
:
295 case AUDIO_ENCODING_ALAW
:
298 case AUDIO_ENCODING_SLINEAR
:
301 case AUDIO_ENCODING_SLINEAR_LE
:
302 if (tmpenc
.precision
== 16)
307 case AUDIO_ENCODING_SLINEAR_BE
:
308 if (tmpenc
.precision
== 16)
313 case AUDIO_ENCODING_ULINEAR
:
316 case AUDIO_ENCODING_ULINEAR_LE
:
317 if (tmpenc
.precision
== 16)
322 case AUDIO_ENCODING_ULINEAR_BE
:
323 if (tmpenc
.precision
== 16)
328 case AUDIO_ENCODING_ADPCM
:
329 idat
|= AFMT_IMA_ADPCM
;
337 case SNDCTL_DSP_GETOSPACE
:
338 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
341 setblocksize(fd
, &tmpinfo
);
342 bufinfo
.fragsize
= tmpinfo
.blocksize
;
343 bufinfo
.fragments
= tmpinfo
.hiwat
-
344 (tmpinfo
.play
.seek
+ tmpinfo
.blocksize
- 1)/tmpinfo
.blocksize
;
345 bufinfo
.fragstotal
= tmpinfo
.hiwat
;
346 bufinfo
.bytes
= tmpinfo
.hiwat
* tmpinfo
.blocksize
- tmpinfo
.play
.seek
;
347 *(struct audio_buf_info
*)argp
= bufinfo
;
349 case SNDCTL_DSP_GETISPACE
:
350 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
353 setblocksize(fd
, &tmpinfo
);
354 bufinfo
.fragsize
= tmpinfo
.blocksize
;
355 bufinfo
.fragments
= tmpinfo
.hiwat
-
356 (tmpinfo
.record
.seek
+ tmpinfo
.blocksize
- 1)/tmpinfo
.blocksize
;
357 bufinfo
.fragstotal
= tmpinfo
.hiwat
;
358 bufinfo
.bytes
= tmpinfo
.hiwat
* tmpinfo
.blocksize
- tmpinfo
.record
.seek
;
359 *(struct audio_buf_info
*)argp
= bufinfo
;
361 case SNDCTL_DSP_NONBLOCK
:
363 retval
= ioctl(fd
, FIONBIO
, &idat
);
367 case SNDCTL_DSP_GETCAPS
:
368 retval
= ioctl(fd
, AUDIO_GETPROPS
, &idata
);
371 idat
= DSP_CAP_TRIGGER
; /* pretend we have trigger */
372 if (idata
& AUDIO_PROP_FULLDUPLEX
)
373 idat
|= DSP_CAP_DUPLEX
;
374 if (idata
& AUDIO_PROP_MMAP
)
375 idat
|= DSP_CAP_MMAP
;
379 case SNDCTL_DSP_GETTRIGGER
:
380 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
383 idat
= (tmpinfo
.play
.pause
? 0 : PCM_ENABLE_OUTPUT
) |
384 (tmpinfo
.record
.pause
? 0 : PCM_ENABLE_INPUT
);
385 retval
= copyout(&idat
, SCARG(uap
, data
), sizeof idat
);
389 case SNDCTL_DSP_SETTRIGGER
:
390 AUDIO_INITINFO(&tmpinfo
);
391 retval
= copyin(SCARG(uap
, data
), &idat
, sizeof idat
);
394 tmpinfo
.play
.pause
= (idat
& PCM_ENABLE_OUTPUT
) == 0;
395 tmpinfo
.record
.pause
= (idat
& PCM_ENABLE_INPUT
) == 0;
396 (void) ioctl(fd
, AUDIO_SETINFO
, &tmpinfo
);
397 retval
= copyout(&idat
, SCARG(uap
, data
), sizeof idat
);
402 case SNDCTL_DSP_GETTRIGGER
:
403 case SNDCTL_DSP_SETTRIGGER
:
404 /* XXX Do nothing for now. */
405 INTARG
= PCM_ENABLE_OUTPUT
;
408 case SNDCTL_DSP_GETIPTR
:
409 retval
= ioctl(fd
, AUDIO_GETIOFFS
, &tmpoffs
);
412 cntinfo
.bytes
= tmpoffs
.samples
;
413 cntinfo
.blocks
= tmpoffs
.deltablks
;
414 cntinfo
.ptr
= tmpoffs
.offset
;
415 *(struct count_info
*)argp
= cntinfo
;
417 case SNDCTL_DSP_GETOPTR
:
418 retval
= ioctl(fd
, AUDIO_GETOOFFS
, &tmpoffs
);
421 cntinfo
.bytes
= tmpoffs
.samples
;
422 cntinfo
.blocks
= tmpoffs
.deltablks
;
423 cntinfo
.ptr
= tmpoffs
.offset
;
424 *(struct count_info
*)argp
= cntinfo
;
426 case SNDCTL_DSP_SETDUPLEX
:
428 retval
= ioctl(fd
, AUDIO_SETFD
, &idat
);
432 case SNDCTL_DSP_GETODELAY
:
433 retval
= ioctl(fd
, AUDIO_GETBUFINFO
, &tmpinfo
);
436 idat
= tmpinfo
.play
.seek
+ tmpinfo
.blocksize
/ 2;
439 case SNDCTL_DSP_PROFILE
:
440 /* This gives just a hint to the driver,
441 * implementing it as a NOP is ok
444 case SNDCTL_DSP_MAPINBUF
:
445 case SNDCTL_DSP_MAPOUTBUF
:
446 case SNDCTL_DSP_SETSYNCRO
:
448 return -1; /* XXX unimplemented */
458 /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices
459 * some will not be available to Linux */
460 #define NETBSD_MAXDEVS 64
461 struct audiodevinfo
{
464 int16_t devmap
[SOUND_MIXER_NRDEVICES
],
465 rdevmap
[NETBSD_MAXDEVS
];
466 char names
[NETBSD_MAXDEVS
][MAX_AUDIO_DEV_LEN
];
467 int enum2opaque
[NETBSD_MAXDEVS
];
468 u_long devmask
, recmask
, stereomask
;
474 opaque_to_enum(struct audiodevinfo
*di
, audio_mixer_name_t
*label
, int opq
)
478 for (i
= 0; i
< NETBSD_MAXDEVS
; i
++) {
479 o
= di
->enum2opaque
[i
];
482 if (o
== -1 && label
!= NULL
&&
483 !strncmp(di
->names
[i
], label
->name
, sizeof di
->names
[i
])) {
484 di
->enum2opaque
[i
] = opq
;
488 if (i
>= NETBSD_MAXDEVS
)
490 /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/
495 enum_to_ord(struct audiodevinfo
*di
, int enm
)
497 if (enm
>= NETBSD_MAXDEVS
)
500 /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/
501 return (di
->enum2opaque
[enm
]);
505 enum_to_mask(struct audiodevinfo
*di
, int enm
)
508 if (enm
>= NETBSD_MAXDEVS
)
511 m
= di
->enum2opaque
[enm
];
514 /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/
519 * Collect the audio device information to allow faster
520 * emulation of the Linux mixer ioctls. Cache the information
521 * to eliminate the overhead of repeating all the ioctls needed
522 * to collect the information.
524 static struct audiodevinfo
*
533 { AudioNmicrophone
, SOUND_MIXER_MIC
},
534 { AudioNline
, SOUND_MIXER_LINE
},
535 { AudioNcd
, SOUND_MIXER_CD
},
536 { AudioNdac
, SOUND_MIXER_PCM
},
537 { AudioNaux
, SOUND_MIXER_LINE1
},
538 { AudioNrecord
, SOUND_MIXER_IMIX
},
539 { AudioNmaster
, SOUND_MIXER_VOLUME
},
540 { AudioNtreble
, SOUND_MIXER_TREBLE
},
541 { AudioNbass
, SOUND_MIXER_BASS
},
542 { AudioNspeaker
, SOUND_MIXER_SPEAKER
},
543 /* { AudioNheadphone, ?? },*/
544 { AudioNoutput
, SOUND_MIXER_OGAIN
},
545 { AudioNinput
, SOUND_MIXER_IGAIN
},
546 /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/
547 /* { AudioNstereo, ?? },*/
548 /* { AudioNmono, ?? },*/
549 { AudioNfmsynth
, SOUND_MIXER_SYNTH
},
550 /* { AudioNwave, SOUND_MIXER_PCM },*/
551 { AudioNmidi
, SOUND_MIXER_SYNTH
},
552 /* { AudioNmixerout, ?? },*/
555 static struct audiodevinfo devcache
= { 0 };
556 struct audiodevinfo
*di
= &devcache
;
560 /* Figure out what device it is so we can check if the
561 * cached data is valid.
563 if (fstat(fd
, &sb
) < 0)
565 if (di
->done
&& di
->dev
== sb
.st_dev
)
575 for(i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
577 for(i
= 0; i
< NETBSD_MAXDEVS
; i
++) {
579 di
->names
[i
][0] = '\0';
580 di
->enum2opaque
[i
] = -1;
582 for(i
= 0; i
< NETBSD_MAXDEVS
; i
++) {
584 if (ioctl(fd
, AUDIO_MIXER_DEVINFO
, &mi
) < 0)
587 case AUDIO_MIXER_VALUE
:
588 for(dp
= devs
; dp
->name
; dp
++) {
589 if (strcmp(dp
->name
, mi
.label
.name
) == 0)
591 dlen
= strlen(dp
->name
);
592 mlen
= strlen(mi
.label
.name
);
594 && mi
.label
.name
[mlen
-dlen
-1] == '.'
595 && strcmp(dp
->name
, mi
.label
.name
+ mlen
- dlen
) == 0)
599 di
->devmap
[dp
->code
] = i
;
600 di
->rdevmap
[i
] = dp
->code
;
601 di
->devmask
|= 1 << dp
->code
;
602 if (mi
.un
.v
.num_channels
== 2)
603 di
->stereomask
|= 1 << dp
->code
;
604 strncpy(di
->names
[i
], mi
.label
.name
,
605 sizeof di
->names
[i
]);
610 for(i
= 0; i
< NETBSD_MAXDEVS
; i
++) {
612 if (ioctl(fd
, AUDIO_MIXER_DEVINFO
, &mi
) < 0)
614 if (strcmp(mi
.label
.name
, AudioNsource
) != 0)
618 case AUDIO_MIXER_ENUM
:
619 for(j
= 0; j
< mi
.un
.e
.num_mem
; j
++) {
620 e
= opaque_to_enum(di
,
621 &mi
.un
.e
.member
[j
].label
,
622 mi
.un
.e
.member
[j
].ord
);
624 di
->recmask
|= 1 << di
->rdevmap
[e
];
626 di
->caps
= SOUND_CAP_EXCL_INPUT
;
628 case AUDIO_MIXER_SET
:
629 for(j
= 0; j
< mi
.un
.s
.num_mem
; j
++) {
630 e
= opaque_to_enum(di
,
631 &mi
.un
.s
.member
[j
].label
,
632 mi
.un
.s
.member
[j
].mask
);
634 di
->recmask
|= 1 << di
->rdevmap
[e
];
643 mixer_ioctl(int fd
, unsigned long com
, void *argp
)
645 struct audiodevinfo
*di
;
646 struct mixer_info
*omi
;
647 struct audio_device adev
;
652 int l
, r
, n
, error
, e
;
661 idat
= SOUND_VERSION
;
663 case SOUND_MIXER_INFO
:
664 case SOUND_OLD_MIXER_INFO
:
665 error
= ioctl(fd
, AUDIO_GETDEV
, &adev
);
669 if (com
== SOUND_MIXER_INFO
)
670 omi
->modify_counter
= 1;
671 strncpy(omi
->id
, adev
.name
, sizeof omi
->id
);
672 strncpy(omi
->name
, adev
.name
, sizeof omi
->name
);
674 case SOUND_MIXER_READ_RECSRC
:
675 if (di
->source
== -1)
678 if (di
->caps
& SOUND_CAP_EXCL_INPUT
) {
679 mc
.type
= AUDIO_MIXER_ENUM
;
680 retval
= ioctl(fd
, AUDIO_MIXER_READ
, &mc
);
683 e
= opaque_to_enum(di
, NULL
, mc
.un
.ord
);
685 idat
= 1 << di
->rdevmap
[e
];
687 mc
.type
= AUDIO_MIXER_SET
;
688 retval
= ioctl(fd
, AUDIO_MIXER_READ
, &mc
);
691 e
= opaque_to_enum(di
, NULL
, mc
.un
.mask
);
693 idat
= 1 << di
->rdevmap
[e
];
696 case SOUND_MIXER_READ_DEVMASK
:
699 case SOUND_MIXER_READ_RECMASK
:
702 case SOUND_MIXER_READ_STEREODEVS
:
703 idat
= di
->stereomask
;
705 case SOUND_MIXER_READ_CAPS
:
708 case SOUND_MIXER_WRITE_RECSRC
:
709 case SOUND_MIXER_WRITE_R_RECSRC
:
710 if (di
->source
== -1)
714 if (di
->caps
& SOUND_CAP_EXCL_INPUT
) {
715 mc
.type
= AUDIO_MIXER_ENUM
;
716 for(i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
719 if (i
>= SOUND_MIXER_NRDEVICES
||
722 mc
.un
.ord
= enum_to_ord(di
, di
->devmap
[i
]);
724 mc
.type
= AUDIO_MIXER_SET
;
726 for(i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++) {
727 if (idat
& (1 << i
)) {
728 if (di
->devmap
[i
] == -1)
730 mc
.un
.mask
|= enum_to_mask(di
, di
->devmap
[i
]);
734 return ioctl(fd
, AUDIO_MIXER_WRITE
, &mc
);
736 if (MIXER_READ(SOUND_MIXER_FIRST
) <= com
&&
737 com
< MIXER_READ(SOUND_MIXER_NRDEVICES
)) {
739 if (di
->devmap
[n
] == -1)
741 mc
.dev
= di
->devmap
[n
];
742 mc
.type
= AUDIO_MIXER_VALUE
;
744 mc
.un
.value
.num_channels
= di
->stereomask
& (1<<n
) ? 2 : 1;
745 retval
= ioctl(fd
, AUDIO_MIXER_READ
, &mc
);
748 if (mc
.type
!= AUDIO_MIXER_VALUE
)
750 if (mc
.un
.value
.num_channels
!= 2) {
751 l
= r
= mc
.un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
];
753 l
= mc
.un
.value
.level
[AUDIO_MIXER_LEVEL_LEFT
];
754 r
= mc
.un
.value
.level
[AUDIO_MIXER_LEVEL_RIGHT
];
756 idat
= TO_OSSVOL(l
) | (TO_OSSVOL(r
) << 8);
758 } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST
) <= com
&&
759 com
< MIXER_WRITE_R(SOUND_MIXER_NRDEVICES
)) ||
760 (MIXER_WRITE(SOUND_MIXER_FIRST
) <= com
&&
761 com
< MIXER_WRITE(SOUND_MIXER_NRDEVICES
))) {
763 if (di
->devmap
[n
] == -1)
766 l
= FROM_OSSVOL( idat
& 0xff);
767 r
= FROM_OSSVOL((idat
>> 8) & 0xff);
768 mc
.dev
= di
->devmap
[n
];
769 mc
.type
= AUDIO_MIXER_VALUE
;
770 if (di
->stereomask
& (1<<n
)) {
771 mc
.un
.value
.num_channels
= 2;
772 mc
.un
.value
.level
[AUDIO_MIXER_LEVEL_LEFT
] = l
;
773 mc
.un
.value
.level
[AUDIO_MIXER_LEVEL_RIGHT
] = r
;
775 mc
.un
.value
.num_channels
= 1;
776 mc
.un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
] = (l
+r
)/2;
778 retval
= ioctl(fd
, AUDIO_MIXER_WRITE
, &mc
);
781 if (MIXER_WRITE(SOUND_MIXER_FIRST
) <= com
&&
782 com
< MIXER_WRITE(SOUND_MIXER_NRDEVICES
))
795 * Check that the blocksize is a power of 2 as OSS wants.
796 * If not, set it to be.
799 setblocksize(int fd
, struct audio_info
*info
)
801 struct audio_info set
;
804 if (info
->blocksize
& (info
->blocksize
-1)) {
805 for(s
= 32; s
< info
->blocksize
; s
<<= 1)
807 AUDIO_INITINFO(&set
);
809 ioctl(fd
, AUDIO_SETINFO
, &set
);
810 ioctl(fd
, AUDIO_GETBUFINFO
, info
);