1 /*
2 * $Id: driver_oss.c,v 1.4 2003/08/31 13:32:02 sakari Exp $
3 *
4 * The OSS mixer driver for Umix.
5 *
6 * Copyright (C) 2002 Sakari Lehtonen <sakari@ionstream.fi>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 */
23
24 #include "umix_common.h"
25
26 /* Hmm not sure about the *BSD includes */
27 #if defined(__linux__)
28 #include <sys/soundcard.h>
29 #elif defined(__FreeBSD__)
30 #include <sys/soundcard.h>
31 #elif defined(__NetBSD__)
32 #include <soundcard.h>
33 #endif
34
35 #define OSS_CHECKMASK(chan, mask) (((1 << (chan)) & (mask)) ? 0 : 1)
36
37 enum
38 {
39 OSS_CHAN_RECSRC = (1<<0), /* is a recording source */
40 OSS_CHAN_RECORD = (1<<1), /* can be a recording source */
41 OSS_CHAN_STEREO = (1<<2), /* supports stereo mixing */
42 OSS_CHAN_VB_CHANGED = (1<<3), /* volume or balance has changed */
43 OSS_CHAN_REC_CHANGED = (1<<4), /* record sources changed */
44 OSS_MAX_CHAN = 32 /* max number of channels for OSS */
45 };
46
47 enum
48 {
49 OSS_UPD_READ, /* read values from the device */
50 OSS_UPD_WRITE, /* write values to the device */
51 OSS_UPD_REC /* read / write record sources */
52 };
53
54 /* TODO: test if definitions in soundcard.h are actually defined, and
55 * include own copies if not */
56
57 /* OSS channel structure */
58 typedef struct oss_channel oss_channel;
59 struct oss_channel
60 {
61 volume vb;
62 int flags;
63 char name[16];
64 char label[16];
65 };
66
67 /* the structure that holds all OSS specific information */
68 typedef struct oss_mixer_info oss_mixer_info;
69 struct oss_mixer_info
70 {
71 int fd;
72 int devmask;
73 int recmask;
74 int recsrc;
75 int stmask;
76 int numchan;
77 int modifycount;
78 int curr_chan;
79 char path[BUFLEN];
80 char name[TINYSTRLEN];
81 oss_channel chan[OSS_MAX_CHAN];
82 };
83
84 /* internal variables */
85
86 /* global OSS variables that are shared between different OSS mixer
87 * devices */
88 static int curr_mixer = 0;
89 static int mixer_count = 0;
90 static int mixers_found = 0;
91
92 static const char *chanlabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
93 static const char *channames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
94
95 static const char *driverstr = "OSS";
96
97 /* the internal structure table for OSS mixer information */
98 static oss_mixer_info *oss_mixers[MAX_MIXERS];
99
100 /* a pointer pointing always to the current mixer, used for optimization */
101 struct oss_mixer_info *mixer_ptr;
102
103 /* internal function prototypes */
104 static int oss_readlevel(void);
105 static int oss_writelevel(int level);
106 static int oss_update(int mode);
107 static int oss_check_update(void);
108
109 /* exported function prototypes */
110 int oss_open(const char *path);
111 int oss_init(void);
112 int oss_close(void);
113
114 int oss_opt_to_chan_num(const char *str);
115
116 int oss_get_curr_mix(void);
117 int oss_set_curr_mix(int mixer);
118 int oss_get_curr_chan(void);
119 int oss_set_curr_chan(int ch);
120
121 float oss_get_vol(void);
122 void oss_set_vol(float vol);
123
124 float oss_get_bal(void);
125 void oss_set_bal(float bal);
126
127 int oss_set_rec(int state);
128
129 int oss_is_stereo(void);
130 int oss_is_recsrc(void);
131 int oss_is_record(void);
132 int oss_is_mute(void);
133
134 const char *oss_get_mix_name(void);
135 const char *oss_get_mix_path(void);
136 const char *oss_get_mix_driver(void);
137 int oss_get_mix_numchan(void);
138
139 int oss_get_mix_status(void);
140
141 const char *oss_get_ch_name(void);
142 const char *oss_get_ch_label(void);
143
144 /* the function pointers */
145 umixer_func oss_func =
146 {
147 oss_open,
148 oss_init,
149 oss_close,
150
151 oss_opt_to_chan_num,
152
153 oss_get_curr_mix,
154 oss_set_curr_mix,
155
156 oss_get_curr_chan,
157 oss_set_curr_chan,
158
159 oss_get_vol,
160 oss_set_vol,
161
162 oss_get_bal,
163 oss_set_bal,
164
165 oss_set_rec,
166
167 oss_is_stereo,
168 oss_is_recsrc,
169 oss_is_record,
170 oss_is_mute,
171
172 oss_get_mix_name,
173 oss_get_mix_path,
174 oss_get_mix_driver,
175 oss_get_mix_numchan,
176 oss_get_mix_status,
177
178 oss_get_ch_name,
179 oss_get_ch_label
180 };
181
oss_get_mixer_func()182 umixer_func *oss_get_mixer_func()
183 {
184 return &oss_func;
185 };
186
187 /* opens and returns the file descriptor for the mixer device specified
188 * in path */
oss_open(const char * path)189 int oss_open(const char *path)
190 {
191 int fd;
192 char devpath[STRLEN];
193 char buf[TINYSTRLEN];
194
195 /* copy the path to the device path */
196 strncpy(devpath, path, sizeof(devpath));
197
198 /* try to init /dev/mixer1 /dev/mixer2 etc if more than one mixer
199 * already */
200 if (mixer_count > 0)
201 {
202 snprintf(buf, sizeof(buf), "%d", mixer_count);
203 strcat(devpath, buf);
204
205 /* find out the total number of mixers available to OSS */
206 if (mixers_found < 1)
207 {
208 static int i;
209
210 for (i=1; i<MAX_MIXERS; i++)
211 {
212 snprintf(buf, sizeof(buf), "%s%d", path, i);
213 if ((fd = eopen(buf, O_RDWR, 0)) != -1)
214 {
215 mixers_found++;
216 eclose(fd);
217 }
218 else
219 break;
220 }
221 }
222 if (mixer_count > mixers_found)
223 return EMIXALLOPEN;
224 }
225
226 #ifdef UMIX_DEBUG
227 err_msg("oss_open: trying to open %s (%s)", devpath, path);
228 #endif
229 /* open the mixer device */
230 if ((fd = eopen(devpath, O_RDWR, 0)) == -1)
231 {
232 err_msg("%s: %s", devpath, strerror(errno));
233 return EMIXERR;
234 }
235
236 oss_mixers[mixer_count] = emalloc(sizeof(oss_mixer_info));
237 if (oss_mixers[mixer_count] == NULL)
238 return EMIXERR;
239 oss_mixers[mixer_count]->fd = fd;
240
241 /* copy the device path */
242 strncpy(oss_mixers[mixer_count]->path,
243 devpath, sizeof(oss_mixers[mixer_count]->path));
244
245 #ifdef UMIX_DEBUG
246 err_msg("oss_open: opened %s with fd %d", devpath, fd);
247 #endif
248
249 return EMIXOK;
250 }
251
252 /* Initializes the current mixer device. The device must be opened with
253 * oss_open() before initializing. */
oss_init(void)254 int oss_init(void)
255 {
256 #if 0
257 /* this is an OSS structure, defined in soundcard.h */
258 struct mixer_info oss_info;
259 #endif
260 int old_mixer;
261 int ch;
262 int i;
263
264 #ifdef UMIX_DEBUG
265 err_msg("oss_init: trying to init mixer #%d", mixer_count);
266 #endif
267 /* mixer_ptr points to the mixer that is to be initialized, and we
268 * don't want to init already initialized mixers, so current mixer
269 * is set to mixer_count and then restored after init */
270 old_mixer = curr_mixer;
271 oss_set_curr_mix(mixer_count);
272
273 /* read the device mask so we know what channels are in use */
274 if( eioctl(mixer_ptr->fd, SOUND_MIXER_READ_DEVMASK,
275 &mixer_ptr->devmask) == -1 )
276 return -1;
277
278 /* read the supported recording sources, this is not fatal if fails */
279 if( eioctl(mixer_ptr->fd, SOUND_MIXER_READ_RECMASK,
280 &mixer_ptr->recmask) == -1 )
281 return -1;
282
283 /* read the current recording sources, not fatal */
284 eioctl(mixer_ptr->fd, SOUND_MIXER_READ_RECSRC, &mixer_ptr->recsrc);
285
286 /* read the stereo support mask, not fatal */
287 eioctl(mixer_ptr->fd, SOUND_MIXER_READ_STEREODEVS, &mixer_ptr->stmask);
288
289 /* allocate channels */
290 mixer_ptr->numchan = SOUND_MIXER_NRDEVICES;
291 mixer_ptr->numchan = CLAMP(mixer_ptr->numchan,0,SOUND_MIXER_NRDEVICES);
292
293 #if 0
294 /* get the mixer name */
295 eioctl(mixer_ptr->fd, SOUND_MIXER_INFO, &oss_info);
296 strncpy(mixer_ptr->name, oss_info.name, sizeof(mixer_ptr->name));
297 #else
298 strncpy(mixer_ptr->name, "", 1);
299 #endif
300
301 mixer_ptr->curr_chan = 0;
302 #ifdef UMIX_DEBUG
303 err_msg("oss_init: %s::%s", oss_get_mix_name(), oss_get_mix_path());
304 #endif
305 for (i=0, ch=0; i<mixer_ptr->numchan; i++)
306 {
307 if (OSS_CHECKMASK(i, mixer_ptr->devmask) == 0)
308 {
309 /* the real channel number is stored in the
310 * second byte of the flags. The real channel number
311 * is used when writing/reading levels to the
312 * OSS device. */
313 mixer_ptr->chan[ch].flags = i;
314 mixer_ptr->chan[ch].flags <<= 8;
315
316 /* check available channel options */
317 if (OSS_CHECKMASK(i, mixer_ptr->recsrc) == 0)
318 mixer_ptr->chan[ch].flags |= OSS_CHAN_RECSRC;
319 if (OSS_CHECKMASK(i, mixer_ptr->recmask) == 0)
320 mixer_ptr->chan[ch].flags |= OSS_CHAN_RECORD;
321 if (OSS_CHECKMASK(i, mixer_ptr->stmask) == 0)
322 mixer_ptr->chan[ch].flags |= OSS_CHAN_STEREO;
323
324 strncpy(mixer_ptr->chan[ch].name, channames[i],
325 sizeof(mixer_ptr->chan[ch].name));
326 strncpy(mixer_ptr->chan[ch].label, chanlabels[i],
327 sizeof(mixer_ptr->chan[ch].label));
328 ch++;
329 }
330 }
331 mixer_ptr->numchan = ch;
332 /* get the volumes for every channel */
333 for (i=0; i<mixer_ptr->numchan; i++)
334 {
335 oss_set_curr_chan(i);
336 oss_update(OSS_UPD_READ);
337 }
338 #if 0
339 /* init the modify counter */
340 eioctl(mixer_ptr->fd, SOUND_MIXER_INFO, &oss_info);
341 mixer_ptr->modifycount = oss_info.modify_counter;
342 #endif
343
344 #ifdef UMIX_DEBUG
345 err_msg("oss_init: initialized #%d with %d channels",
346 curr_mixer, mixer_ptr->numchan);
347 #endif
348 mixer_count++;
349 /* restore the old mixer */
350 oss_set_curr_mix(old_mixer);
351 return 0;
352 }
353
oss_close(void)354 int oss_close(void)
355 {
356 if (mixer_count < 1)
357 return -1;
358
359 eclose(oss_mixers[curr_mixer]->fd);
360 free(oss_mixers[curr_mixer]);
361 mixer_count--;
362 curr_mixer = CLAMP(curr_mixer, 0, mixer_count);
363
364 #ifdef UMIX_DEBUG
365 err_msg("oss_close: closed mixer #%d", curr_mixer);
366 #endif
367
368 return 0;
369 }
370
371 /* TODO: maybe move this to mixer.c and make a function that
372 * returns a general table with channel names and numbers, so we
373 * don't have to copy this code to every mixer driver */
oss_opt_to_chan_num(const char * str)374 int oss_opt_to_chan_num(const char *str)
375 {
376 int i;
377 size_t n;
378 char *chname;
379
380 for (i=0; i<mixer_ptr->numchan; i++)
381 {
382 oss_set_curr_chan(i);
383 chname = mixer_ptr->chan[mixer_ptr->curr_chan].name;
384 n = strlen(str);
385 /* compare only so many letters that the string
386 * to be compared to has, so you can use p instead
387 * of pcm etc */
388 if (strncasecmp(chname, str, n) == 0)
389 return mixer_ptr->curr_chan;
390 }
391
392 return -1;
393 }
394
395 /* checks if the mixer device has been updated, reads the new value from
396 * the device and compares it to the old */
oss_check_update(void)397 static int oss_check_update(void)
398 {
399 #if 0
400 struct mixer_info oss_info;
401
402 eioctl(mixer_ptr->fd, SOUND_MIXER_INFO, &oss_info);
403 if (oss_info.modify_counter != mixer_ptr->modifycount)
404 {
405 mixer_ptr->modifycount = oss_info.modify_counter;
406 return 1;
407 }
408 #endif
409
410 return 0;
411 }
412
oss_get_mix_status(void)413 int oss_get_mix_status(void)
414 {
415 int orig_chan;
416 int i;
417
418 if (oss_check_update() == 1)
419 {
420 orig_chan = mixer_ptr->curr_chan;
421 for (i=0; i<mixer_ptr->numchan; i++)
422 {
423 oss_set_curr_chan(i);
424 oss_update(OSS_UPD_READ);
425 }
426 oss_set_curr_chan(orig_chan);
427
428 return 1;
429 }
430
431 return 0;
432 }
433
oss_get_curr_mix(void)434 int oss_get_curr_mix(void)
435 {
436 return curr_mixer;
437 }
438
oss_set_curr_mix(int mixer)439 int oss_set_curr_mix(int mixer)
440 {
441 if (curr_mixer < 0 || curr_mixer > MAX_MIXERS)
442 return -1;
443
444 curr_mixer = mixer;
445 /* point global mixer_ptr to the current mixer */
446 mixer_ptr = oss_mixers[curr_mixer];
447
448 return 0;
449 }
450
oss_get_curr_chan(void)451 int oss_get_curr_chan(void)
452 {
453 return mixer_ptr->curr_chan;
454 }
455
oss_set_curr_chan(int ch)456 int oss_set_curr_chan(int ch)
457 {
458 if ((ch < 0) || (ch > mixer_ptr->numchan))
459 return -1;
460
461 mixer_ptr->curr_chan = ch;
462
463 return 0;
464 }
465
466 /* reads the level from the device */
oss_readlevel(void)467 static int oss_readlevel(void)
468 {
469 int level;
470 int realch;
471
472 realch = mixer_ptr->chan[mixer_ptr->curr_chan].flags>>8;
473 if (eioctl(mixer_ptr->fd, MIXER_READ(realch), &level) == -1)
474 return -1;
475
476 return level;
477 }
478
479 /* writes the the level to the device */
oss_writelevel(int level)480 static int oss_writelevel(int level)
481 {
482 int realch;
483
484 realch = mixer_ptr->chan[mixer_ptr->curr_chan].flags>>8;
485
486 if (eioctl(mixer_ptr->fd, MIXER_WRITE(realch), &level) == -1)
487 return -1;
488
489 return 0;
490 }
491
492 /* Update values from or to the device to the internal oss_mixers[] structure
493 * pointers, depends on the mode. Mode OSS_UPD_WRITE writes updated values
494 * to the device, OSS_UPD_READ reads the volume for current channel to the
495 * structures and OSS_UPD_REC updates recording sources from the device. */
496
497 /* TODO: optimization: maybe figure a better way to buffer writes to the
498 * device, and check if the write is really needed. */
oss_update(int mode)499 static int oss_update(int mode)
500 {
501 int level;
502 int l_left;
503 int l_right;
504 int curr_chan;
505 float left;
506 float right;
507
508 curr_chan = mixer_ptr->curr_chan;
509
510 /* write to device */
511 if (mode == OSS_UPD_WRITE)
512 {
513 vb_to_lr(mixer_ptr->chan[curr_chan].vb.volume,
514 mixer_ptr->chan[curr_chan].vb.balance,
515 &left, &right);
516
517 l_left = (int)left;
518 l_right = (int)right;
519
520 /* left is the lower byte, right the higher */
521 level = (l_left | (l_right << 8));
522 oss_writelevel(level);
523 }
524 /* read from device */
525 else if (mode == OSS_UPD_READ)
526 {
527 level = oss_readlevel();
528 /* left is the lower byte, right the higher */
529 l_left = (level & 0xFF);
530 l_right = ((level >> 8) & 0xFF);
531
532 left = (float)(l_left);
533 right = (float)(l_right);
534
535 lr_to_vb(left, right,
536 &mixer_ptr->chan[curr_chan].vb.volume,
537 &mixer_ptr->chan[curr_chan].vb.balance);
538 }
539 /* update record source flags. We need to do this because when
540 * setting record sources with chan_set_rec(CHAN_RECSRC), the
541 * old record source channel may be changed to play mode, and we
542 * need to read the current record sources from the device and
543 * update our internal flags to match with this */
544 else if (mode == OSS_UPD_REC)
545 {
546 int i, realch;
547
548 /* read the current recording sources */
549 eioctl(mixer_ptr->fd, SOUND_MIXER_READ_RECSRC,
550 &mixer_ptr->recsrc);
551
552 for (i=0; i<mixer_ptr->numchan; i++)
553 {
554 realch = mixer_ptr->chan[i].flags >> 8;
555 if (OSS_CHECKMASK(realch, mixer_ptr->recsrc) == 0)
556 {
557 mixer_ptr->recsrc |= (1<<realch);
558 mixer_ptr->chan[i].flags |= OSS_CHAN_RECSRC;
559 }
560 else
561 {
562 mixer_ptr->recsrc &= ~(1<<realch);
563 mixer_ptr->chan[i].flags &= ~OSS_CHAN_RECSRC;
564 }
565 }
566 }
567
568 return 0;
569 }
570
oss_set_vol(float vol)571 void oss_set_vol(float vol)
572 {
573 mixer_ptr->chan[mixer_ptr->curr_chan].vb.volume = vol;
574 oss_update(OSS_UPD_WRITE);
575 }
576
oss_get_vol(void)577 float oss_get_vol(void)
578 {
579 /* check if the device has been updated */
580 if (oss_check_update() == 1)
581 oss_update(OSS_UPD_READ);
582
583 return mixer_ptr->chan[mixer_ptr->curr_chan].vb.volume;
584 }
585
oss_set_bal(float bal)586 void oss_set_bal(float bal)
587 {
588 mixer_ptr->chan[mixer_ptr->curr_chan].vb.balance = bal;
589 oss_update(OSS_UPD_WRITE);
590 }
591
oss_get_bal(void)592 float oss_get_bal(void)
593 {
594 /* check if the device has been updated */
595 if (oss_check_update() == 1)
596 oss_update(OSS_UPD_READ);
597
598 return mixer_ptr->chan[mixer_ptr->curr_chan].vb.balance;
599 }
600
601 /* Sets the recording state for the current channel. State CHAN_RECSRC
602 * tries to set the current channel as a recording source, else it
603 * tries to set the current channel not as a recording source */
oss_set_rec(int state)604 int oss_set_rec(int state)
605 {
606 int recsrc;
607 int realch;
608
609 recsrc = mixer_ptr->recsrc;
610 realch = mixer_ptr->chan[mixer_ptr->curr_chan].flags>>8;
611
612 if (state == CHAN_RECSRC)
613 recsrc |= (1<<realch);
614 else
615 recsrc &= ~(1<<realch);
616
617 if (eioctl(mixer_ptr->fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
618 return -1;
619
620 oss_update(OSS_UPD_REC);
621
622 return 0;
623 }
624
625 /* check if the current channel supports stereo mixing */
oss_is_stereo(void)626 int oss_is_stereo(void)
627 {
628 if (mixer_ptr->chan[mixer_ptr->curr_chan].flags & OSS_CHAN_STEREO)
629 return 1;
630 else
631 return 0;
632 }
633
634 /* check if the current channel is a recording source */
oss_is_recsrc(void)635 int oss_is_recsrc(void)
636 {
637 oss_update(OSS_UPD_REC);
638
639 if (mixer_ptr->chan[mixer_ptr->curr_chan].flags & OSS_CHAN_RECSRC)
640 return 1;
641 else
642 return 0;
643 }
644
645 /* check if the current channel can be a recording source */
oss_is_record(void)646 int oss_is_record(void)
647 {
648 if (mixer_ptr->chan[mixer_ptr->curr_chan].flags & OSS_CHAN_RECORD)
649 return 1;
650 else
651 return 0;
652 }
653
654 /* check if volume for current channel is 0 */
oss_is_mute(void)655 int oss_is_mute(void)
656 {
657 if (mixer_ptr->chan[mixer_ptr->curr_chan].vb.volume == 0)
658 return 1;
659 else
660 return 0;
661 }
662
663 /* get the mixer name */
oss_get_mix_name(void)664 const char *oss_get_mix_name(void)
665 {
666 return mixer_ptr->name;
667 }
668
669 /* get the device path */
oss_get_mix_path(void)670 const char *oss_get_mix_path(void)
671 {
672 return mixer_ptr->path;
673 }
674
675 /* get the name of the mixer driver */
oss_get_mix_driver(void)676 const char *oss_get_mix_driver(void)
677 {
678 return driverstr;
679 }
680
681 /* get the number of channels for current mixer */
oss_get_mix_numchan(void)682 int oss_get_mix_numchan(void)
683 {
684 return mixer_ptr->numchan;
685 }
686
687 /* get the channel name */
oss_get_ch_name(void)688 const char *oss_get_ch_name(void)
689 {
690 return mixer_ptr->chan[mixer_ptr->curr_chan].name;
691 }
692
693 /* get the short version of channel name */
oss_get_ch_label(void)694 const char *oss_get_ch_label(void)
695 {
696 return mixer_ptr->chan[mixer_ptr->curr_chan].label;
697 }
698