"Fossies" - the Fresh Open Source Software Archive 
Member "alsa-oss-1.1.8/alsa/mixer.c" (7 Jan 2019, 15525 Bytes) of package /linux/misc/alsa-oss-1.1.8.tar.bz2:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "mixer.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
1.0.28_vs_1.1.6.
1 /*
2 * OSS -> ALSA compatibility layer
3 * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #define _GNU_SOURCE
23
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <sys/stat.h>
27 #include <sys/poll.h>
28 #include <sys/select.h>
29 #include <sys/mman.h>
30 #include <stdarg.h>
31 #include <unistd.h>
32 #include <dlfcn.h>
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <linux/soundcard.h>
39 #include <alsa/asoundlib.h>
40
41 #include "alsa-local.h"
42
43 typedef struct _oss_mixer {
44 int fileno;
45 snd_mixer_t *mix;
46 unsigned int modify_counter;
47 snd_mixer_elem_t *elems[SOUND_MIXER_NRDEVICES];
48 struct _oss_mixer *next;
49 } oss_mixer_t;
50
51 static oss_mixer_t *mixer_fds = NULL;
52
53 static oss_mixer_t *look_for_fd(int fd)
54 {
55 oss_mixer_t *result = mixer_fds;
56 while (result) {
57 if (result->fileno == fd)
58 return result;
59 result = result->next;
60 }
61 return NULL;
62 }
63
64 static void insert_fd(oss_mixer_t *xfd)
65 {
66 xfd->next = mixer_fds;
67 mixer_fds = xfd;
68 }
69
70 static void remove_fd(oss_mixer_t *xfd)
71 {
72 oss_mixer_t *result = mixer_fds, *prev = NULL;
73 while (result) {
74 if (result == xfd) {
75 if (prev == NULL)
76 mixer_fds = xfd->next;
77 else
78 prev->next = xfd->next;
79 return;
80 }
81 prev = result;
82 result = result->next;
83 }
84 assert(0);
85 }
86
87 static int oss_mixer_dev(const char *name, unsigned int index)
88 {
89 static struct {
90 char *name;
91 unsigned int index;
92 } id[SOUND_MIXER_NRDEVICES] = {
93 [SOUND_MIXER_VOLUME] = { "Master", 0 },
94 [SOUND_MIXER_BASS] = { "Tone Control - Bass", 0 },
95 [SOUND_MIXER_TREBLE] = { "Tone Control - Treble", 0 },
96 [SOUND_MIXER_SYNTH] = { "Synth", 0 },
97 [SOUND_MIXER_PCM] = { "PCM", 0 },
98 [SOUND_MIXER_SPEAKER] = { "PC Speaker", 0 },
99 [SOUND_MIXER_LINE] = { "Line", 0 },
100 [SOUND_MIXER_MIC] = { "Mic", 0 },
101 [SOUND_MIXER_CD] = { "CD", 0 },
102 [SOUND_MIXER_IMIX] = { "Monitor Mix", 0 },
103 [SOUND_MIXER_ALTPCM] = { "PCM", 1 },
104 [SOUND_MIXER_RECLEV] = { "-- nothing --", 0 },
105 [SOUND_MIXER_IGAIN] = { "Capture", 0 },
106 [SOUND_MIXER_OGAIN] = { "Playback", 0 },
107 [SOUND_MIXER_LINE1] = { "Aux", 0 },
108 [SOUND_MIXER_LINE2] = { "Aux", 1 },
109 [SOUND_MIXER_LINE3] = { "Aux", 2 },
110 [SOUND_MIXER_DIGITAL1] = { "Digital", 0 },
111 [SOUND_MIXER_DIGITAL2] = { "Digital", 1 },
112 [SOUND_MIXER_DIGITAL3] = { "Digital", 2 },
113 [SOUND_MIXER_PHONEIN] = { "Phone", 0 },
114 [SOUND_MIXER_PHONEOUT] = { "Phone", 1 },
115 [SOUND_MIXER_VIDEO] = { "Video", 0 },
116 [SOUND_MIXER_RADIO] = { "Radio", 0 },
117 [SOUND_MIXER_MONITOR] = { "Monitor", 0 },
118 };
119 unsigned int k;
120 for (k = 0; k < SOUND_MIXER_NRDEVICES; ++k) {
121 if (index == id[k].index &&
122 strcmp(name, id[k].name) == 0)
123 return k;
124 }
125 return -1;
126 }
127
128 int lib_oss_mixer_close(int fd)
129 {
130 int err, result = 0;
131 oss_mixer_t *mixer = look_for_fd(fd);
132 err = snd_mixer_close(mixer->mix);
133 if (err < 0)
134 result = err;
135 remove_fd(mixer);
136 free(mixer);
137 if (result < 0) {
138 errno = -result;
139 result = -1;
140 }
141 close(fd);
142 DEBUG("close(%d) -> %d", fd, result);
143 if (result < 0)
144 DEBUG("(errno=%d)\n", errno);
145 else
146 DEBUG("\n");
147 return 0;
148 }
149
150 static int oss_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned int mask)
151 {
152 oss_mixer_t *mixer = snd_mixer_elem_get_callback_private(elem);
153 if (mask == SND_CTL_EVENT_MASK_REMOVE) {
154 int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
155 snd_mixer_selem_get_index(elem));
156 if (idx >= 0)
157 mixer->elems[idx] = 0;
158 return 0;
159 }
160 if (mask & SND_CTL_EVENT_MASK_VALUE) {
161 mixer->modify_counter++;
162 }
163 return 0;
164 }
165
166 static int oss_mixer_callback(snd_mixer_t *mixer, unsigned int mask,
167 snd_mixer_elem_t *elem)
168 {
169 if (mask & SND_CTL_EVENT_MASK_ADD) {
170 oss_mixer_t *mix = snd_mixer_get_callback_private(mixer);
171 int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
172 snd_mixer_selem_get_index(elem));
173 if (idx >= 0) {
174 mix->elems[idx] = elem;
175 snd_mixer_selem_set_playback_volume_range(elem, 0, 100);
176 snd_mixer_selem_set_capture_volume_range(elem, 0, 100);
177 snd_mixer_elem_set_callback(elem, oss_mixer_elem_callback);
178 snd_mixer_elem_set_callback_private(elem, mix);
179 }
180 }
181 return 0;
182 }
183
184 static int oss_mixer_open(int card, int device, int oflag, mode_t mode ATTRIBUTE_UNUSED)
185 {
186 oss_mixer_t *mixer;
187 int fd = -1;
188 int result;
189 char name[64];
190
191 char *s = getenv("ALSA_OSS_DEBUG");
192 if (s) {
193 alsa_oss_debug = 1;
194 if (alsa_oss_debug_out == NULL) {
195 if (snd_output_stdio_attach(&alsa_oss_debug_out, stderr, 0) < 0)
196 alsa_oss_debug_out = NULL;
197 }
198 }
199 switch (device) {
200 case OSS_DEVICE_MIXER:
201 sprintf(name, "mixer%d", card);
202 break;
203 case OSS_DEVICE_AMIXER:
204 sprintf(name, "amixer%d", card);
205 break;
206 default:
207 errno = ENODEV;
208 return -1;
209 }
210 switch (oflag & O_ACCMODE) {
211 case O_RDONLY:
212 case O_WRONLY:
213 case O_RDWR:
214 break;
215 default:
216 errno = EINVAL;
217 return -1;
218 }
219 fd = open("/dev/null", oflag & O_ACCMODE);
220 assert(fd >= 0);
221 mixer = calloc(1, sizeof(oss_mixer_t));
222 if (!mixer) {
223 errno = -ENOMEM;
224 return -1;
225 }
226 result = snd_mixer_open(&mixer->mix, 0);
227 if (result < 0)
228 goto _error;
229 result = snd_mixer_attach(mixer->mix, name);
230 if (result < 0) {
231 /* try to open the default mixer as fallback */
232 if (card == 0)
233 strcpy(name, "default");
234 else
235 sprintf(name, "hw:%d", card);
236 result = snd_mixer_attach(mixer->mix, name);
237 if (result < 0)
238 goto _error1;
239 }
240 result = snd_mixer_selem_register(mixer->mix, NULL, NULL);
241 if (result < 0)
242 goto _error1;
243 snd_mixer_set_callback(mixer->mix, oss_mixer_callback);
244 snd_mixer_set_callback_private(mixer->mix, mixer);
245 result = snd_mixer_load(mixer->mix);
246 if (result < 0)
247 goto _error1;
248 mixer->fileno = fd;
249 insert_fd(mixer);
250 return fd;
251 _error1:
252 snd_mixer_close(mixer->mix);
253 _error:
254 close(fd);
255 errno = -result;
256 return -1;
257 }
258
259 static int oss_mixer_read_recsrc(oss_mixer_t *mixer, unsigned int *ret)
260 {
261 unsigned int mask = 0;
262 unsigned int k;
263 int err = 0;
264 for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
265 snd_mixer_elem_t *elem = mixer->elems[k];
266 if (elem &&
267 snd_mixer_selem_has_capture_switch(elem)) {
268 int sw;
269 err = snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &sw);
270 if (err < 0)
271 break;
272 if (sw)
273 mask |= 1 << k;
274 }
275 }
276 *ret = mask;
277 return err;
278 }
279
280
281 int lib_oss_mixer_ioctl(int fd, unsigned long cmd, ...)
282 {
283 int err = 0;
284 va_list args;
285 void *arg;
286 oss_mixer_t *mixer = look_for_fd(fd);
287 snd_mixer_t *mix;
288 unsigned int dev;
289
290 if (mixer == NULL) {
291 errno = ENODEV;
292 return -1;
293 }
294 mix = mixer->mix;
295 va_start(args, cmd);
296 arg = va_arg(args, void *);
297 va_end(args);
298 DEBUG("ioctl(%d, ", fd);
299 switch (cmd) {
300 case OSS_GETVERSION:
301 *(int*)arg = SOUND_VERSION;
302 DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
303 break;
304 case SOUND_MIXER_INFO:
305 {
306 mixer_info *info = arg;
307 snd_mixer_handle_events(mix);
308 strcpy(info->id, "alsa-oss");
309 strcpy(info->name, "alsa-oss");
310 info->modify_counter = mixer->modify_counter;
311 DEBUG("SOUND_MIXER_INFO, %p) -> {%s, %s, %d}\n", info, info->id, info->name, info->modify_counter);
312 break;
313 }
314 case SOUND_OLD_MIXER_INFO:
315 {
316 _old_mixer_info *info = arg;
317 strcpy(info->id, "alsa-oss");
318 strcpy(info->name, "alsa-oss");
319 DEBUG("SOUND_OLD_MIXER_INFO, %p) -> {%s, %s}\n", info, info->id, info->name);
320 break;
321 }
322 case SOUND_MIXER_WRITE_RECSRC:
323 {
324 unsigned int k, mask = *(unsigned int *) arg;
325 unsigned int old;
326 int excl = 0;
327 DEBUG("SOUND_MIXER_WRITE_RECSRC, %p) -> [%x]", arg, mask);
328 err = oss_mixer_read_recsrc(mixer, &old);
329 if (err < 0)
330 break;
331 for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
332 snd_mixer_elem_t *elem = mixer->elems[k];
333 if (elem &&
334 snd_mixer_selem_has_capture_switch(elem)) {
335 if (!excl &&
336 snd_mixer_selem_has_capture_switch_exclusive(elem) &&
337 mask & ~old) {
338 mask &= ~old;
339 excl = 1;
340 }
341 err = snd_mixer_selem_set_capture_switch_all(elem, !!(mask & 1 << k));
342 if (err < 0)
343 break;
344 }
345 }
346 if (err < 0)
347 break;
348 goto __read_recsrc;
349 }
350 case SOUND_MIXER_READ_RECSRC:
351 {
352 unsigned int mask;
353 DEBUG("SOUND_MIXER_READ_RECSRC, %p) ->", arg);
354 __read_recsrc:
355 err = oss_mixer_read_recsrc(mixer, &mask);
356 *(int *)arg = mask;
357 DEBUG(" [%x]\n", mask);
358 break;
359 }
360 case SOUND_MIXER_READ_DEVMASK:
361 {
362 int k, mask = 0;
363 for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
364 snd_mixer_elem_t *elem = mixer->elems[k];
365 if (elem &&
366 (snd_mixer_selem_has_playback_volume(elem) ||
367 snd_mixer_selem_has_capture_volume(elem)))
368 mask |= 1 << k;
369 }
370 *(int *)arg = mask;
371 DEBUG("SOUND_MIXER_READ_DEVMASK, %p) -> [%x]\n", arg, mask);
372 break;
373 }
374 case SOUND_MIXER_READ_RECMASK:
375 {
376 int k, mask = 0;
377 for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
378 snd_mixer_elem_t *elem = mixer->elems[k];
379 if (elem &&
380 snd_mixer_selem_has_capture_switch(elem))
381 mask |= 1 << k;
382 }
383 *(int *)arg = mask;
384 DEBUG("SOUND_MIXER_READ_RECMASK, %p) -> [%x]\n", arg, mask);
385 break;
386 }
387 case SOUND_MIXER_READ_STEREODEVS:
388 {
389 int k, mask = 0;
390 for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
391 snd_mixer_elem_t *elem = mixer->elems[k];
392 if (elem &&
393 snd_mixer_selem_has_playback_volume(elem) &&
394 !snd_mixer_selem_is_playback_mono(elem))
395 mask |= 1 << k;
396 }
397 *(int *)arg = mask;
398 DEBUG("SOUND_MIXER_READ_STEREODEVS, %p) -> [%x]\n", arg, mask);
399 break;
400 }
401 case SOUND_MIXER_READ_CAPS:
402 {
403 int k;
404 *(int *)arg = 0;
405 for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
406 snd_mixer_elem_t *elem = mixer->elems[k];
407 if (elem &&
408 snd_mixer_selem_has_capture_switch_exclusive(elem)) {
409 * (int*) arg = SOUND_CAP_EXCL_INPUT;
410 break;
411 }
412 }
413 DEBUG("SOUND_MIXER_READ_CAPS, %p) -> [%x]\n", arg, *(int*) arg);
414 break;
415 }
416 default:
417 if (cmd >= MIXER_WRITE(0) && cmd < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) {
418 snd_mixer_elem_t *elem;
419 long lvol, rvol;
420 dev = cmd & 0xff;
421 lvol = *(int *)arg & 0xff;
422 if (lvol > 100)
423 lvol = 100;
424 rvol = (*(int *)arg >> 8) & 0xff;
425 if (rvol > 100)
426 rvol = 100;
427 DEBUG("SOUND_MIXER_WRITE[%d], %p) -> {%ld, %ld}", dev, arg, lvol, rvol);
428 elem = mixer->elems[dev];
429 if (!elem) {
430 err = -EINVAL;
431 break;
432 }
433 if (snd_mixer_selem_has_playback_volume(elem)) {
434 err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol);
435 if (err < 0)
436 break;
437 if (snd_mixer_selem_is_playback_mono(elem)) {
438 if (snd_mixer_selem_has_playback_switch(elem))
439 err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
440 if (err < 0)
441 break;
442 } else {
443 err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol);
444 if (err < 0)
445 break;
446 if (snd_mixer_selem_has_playback_switch(elem)) {
447 if (snd_mixer_selem_has_playback_switch_joined(elem))
448 err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0 || rvol != 0);
449 else {
450 err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
451 if (err < 0)
452 break;
453 err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol != 0);
454 if (err < 0)
455 break;
456 }
457 }
458 }
459 }
460 if (snd_mixer_selem_has_capture_volume(elem)) {
461 err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol);
462 if (err < 0)
463 break;
464 if (!snd_mixer_selem_is_capture_mono(elem)) {
465 err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol);
466 if (err < 0)
467 break;
468 }
469 }
470 goto __read;
471 }
472 if (cmd >= MIXER_READ(0) && cmd < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
473 snd_mixer_elem_t *elem;
474 long lvol, rvol;
475 int sw;
476 dev = cmd & 0xff;
477 DEBUG("SOUND_MIXER_READ[%d], %p) ->", dev, arg);
478 __read:
479 elem = mixer->elems[dev];
480 if (!elem) {
481 err = -EINVAL;
482 break;
483 }
484 if (snd_mixer_selem_has_playback_volume(elem)) {
485 if (snd_mixer_selem_has_playback_switch(elem)) {
486 err = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &sw);
487 if (err < 0)
488 break;
489 } else {
490 sw = 1;
491 }
492 if (sw) {
493 err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &lvol);
494 if (err < 0)
495 break;
496 } else
497 lvol = 0;
498 if (snd_mixer_selem_is_playback_mono(elem)) {
499 rvol = lvol;
500 } else {
501 if (snd_mixer_selem_has_playback_switch(elem)) {
502 err = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &sw);
503 if (err < 0)
504 break;
505 } else {
506 sw = 1;
507 }
508 if (sw) {
509 err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &rvol);
510 if (err < 0)
511 break;
512 } else
513 rvol = 0;
514 }
515 * (int*) arg = lvol | (rvol << 8);
516 DEBUG("{%ld, %ld}\n", lvol, rvol);
517 break;
518 }
519 if (snd_mixer_selem_has_capture_volume(elem)) {
520 err = snd_mixer_selem_get_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &lvol);
521 if (err < 0)
522 break;
523 if (!snd_mixer_selem_is_capture_mono(elem)) {
524 err = snd_mixer_selem_get_capture_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &rvol);
525 if (err < 0)
526 break;
527 }
528 * (int*) arg = lvol | (rvol << 8);
529 DEBUG("{%ld, %ld}\n", lvol, rvol);
530 break;
531 }
532 }
533 DEBUG("%lx, %p)\n", cmd, arg);
534 err = -ENXIO;
535 break;
536 }
537 if (err >= 0)
538 return 0;
539 errno = -err;
540 return -1;
541 }
542
543 static void error_handler(const char *file ATTRIBUTE_UNUSED,
544 int line ATTRIBUTE_UNUSED,
545 const char *func ATTRIBUTE_UNUSED,
546 int err ATTRIBUTE_UNUSED,
547 const char *fmt ATTRIBUTE_UNUSED,
548 ...)
549 {
550 /* suppress the error message from alsa-lib */
551 }
552
553 int lib_oss_mixer_open(const char *file, int oflag, ...)
554 {
555 int result;
556 int minor, card, device;
557 struct stat s;
558 mode_t mode;
559 va_list args;
560 va_start(args, oflag);
561 mode = va_arg(args, mode_t);
562 va_end(args);
563 result = stat(file, &s);
564 if (result < 0) {
565 if (!strncmp(file, "/dev/mixer", 10))
566 minor = (atoi(file + 10) << 4) | OSS_DEVICE_MIXER;
567 else if (!strncmp(file, "/dev/amixer", 11))
568 minor = (atoi(file + 11) << 4) | OSS_DEVICE_AMIXER;
569 else if (!strncmp(file, "/dev/sound/mixer", 16))
570 minor = (atoi(file + 16) << 4) | OSS_DEVICE_MIXER;
571 else if (!strncmp(file, "/dev/sound/amixer", 17))
572 minor = (atoi(file + 17) << 4) | OSS_DEVICE_AMIXER;
573 else {
574 errno = ENOENT;
575 return -1;
576 }
577 } else {
578 if (!S_ISCHR(s.st_mode) || ((s.st_rdev >> 8) & 0xff) != OSS_MAJOR) {
579 errno = ENOENT;
580 return -1;
581 }
582 minor = s.st_rdev & 0xff;
583 }
584 if (! alsa_oss_debug)
585 snd_lib_error_set_handler(error_handler);
586 card = minor >> 4;
587 device = minor & 0x0f;
588 switch (device) {
589 case OSS_DEVICE_MIXER:
590 case OSS_DEVICE_AMIXER:
591 result = oss_mixer_open(card, device, oflag, mode);
592 DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
593 return result;
594 default:
595 errno = ENOENT;
596 return -1;
597 }
598 }