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