Initial version of alsactl...

This commit is contained in:
Jaroslav Kysela 1998-10-29 22:45:59 +00:00
parent eea08fa810
commit 72ff837506
6 changed files with 1664 additions and 0 deletions

45
alsactl/Makefile Normal file
View file

@ -0,0 +1,45 @@
#
# Makefile for alsactl program
# Copyright (c) 1994-98 by Jaroslav Kysela <perex@jcu.cz>
#
TOPDIR=..
include $(TOPDIR)/Makefile.conf
CFLAGS=
TARGET=alsactl
OBJECTS=alsactl.o setup.o alsactl_parser.o alsactl_lexer.o
# parser / lexer debug
#CFLAGS += -DYYDEBUG
#FLEXFLAGS = -d
#BISONFLAGS = -v
.c.o:
$(CC) $(COPTS) $(CFLAGS) $(INCLUDE) -c -o $*.o $<
all: $(TARGET)
$(TARGET): .depend $(OBJECTS)
$(CC) $(SNDLIB) $(OBJECTS) -o $(TARGET)
alsactl_lexer.c: alsactl_lexer.l
$(FLEX) $(FLEXFLAGS) -i -o$@ $<
alsactl_parser.c: alsactl_parser.y
$(BISON) $(BISONFLAGS) -do $@ $<
clean:
rm -f core alsactl .depend *.o *.orig *~ \
alsactl_lexer.c alsactl_parser.[ch] alsactl_parser.output
.depend:
$(CPP) $(COPTS) $(INCLUDE) -M *.c > .depend
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

195
alsactl/alsactl.c Normal file
View file

@ -0,0 +1,195 @@
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1997 by Perex, APS, University of South Bohemia
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "alsactl.h"
#include <getopt.h>
#include <stdarg.h>
#define HELPID_HELP 1000
#define HELPID_FILE 1001
#define HELPID_DEBUG 1002
#define HELPID_VERSION 1003
extern int yyparse( void );
extern int linecount;
extern FILE *yyin;
extern int yydebug;
int debugflag = 0;
char cfgfile[ 512 ] = ALSACTL_FILE;
void error( const char *fmt, ... )
{
va_list va;
va_start( va, fmt );
fprintf( stderr, "alsactl: " );
vfprintf( stderr, fmt, va );
fprintf( stderr, "\n" );
va_end( va );
}
static void help( void )
{
printf( "Usage: alsactl <options> command\n" );
printf( "\nAvailable options:\n" );
printf( " -h,--help this help\n" );
printf( " -f,--file # configuration file (default " ALSACTL_FILE ")\n" );
printf( " -d,--debug debug mode\n" );
printf( " -v,--version print version of this program\n" );
printf( "\nAvailable commands:\n" );
printf( " store <card #> store current driver setup for one or each soundcards\n" );
printf( " to configuration file\n" );
printf( " restore <card #> restore current driver setup for one or each soundcards\n" );
printf( " from configuration file\n" );
}
static int store_setup( const char *cardname )
{
int err;
if ( !cardname ) {
unsigned int card_mask, idx;
card_mask = snd_cards_mask();
if ( !card_mask ) {
error( "No soundcards found..." );
return 1;
}
soundcard_setup_init();
for ( idx = 0; idx < 32; idx++ ) {
if ( card_mask & (1 << idx) ) { /* find each installed soundcards */
if ( (err = soundcard_setup_collect( idx )) ) {
soundcard_setup_done();
return err;
}
}
}
} else {
int cardno;
cardno = snd_card_name( cardname );
if ( cardno ) {
error( "Cannot find soundcard '%s'...", cardname );
return 1;
}
soundcard_setup_init();
if ( (err = soundcard_setup_load( cfgfile )) )
return err;
if ( (err = soundcard_setup_collect( cardno )) ) {
soundcard_setup_done();
return err;
}
}
err = soundcard_setup_write( cfgfile );
soundcard_setup_done();
return err;
}
static int restore_setup( const char *cardname )
{
int err, idx, cardno = -1;
unsigned int card_mask;
if ( cardname ) {
cardno = snd_card_name( cardname );
if ( cardno < 0 ) {
error( "Cannot find soundcard '%s'...", cardname );
return 1;
}
}
card_mask = snd_cards_mask();
if ( !card_mask ) {
error( "No soundcards found..." );
return 1;
}
soundcard_setup_init();
for ( idx = 0; idx < 32; idx++ ) {
if ( card_mask & (1 << idx) ) { /* find each installed soundcards */
if ( (err = soundcard_setup_collect( idx )) ) {
soundcard_setup_done();
return err;
}
}
}
if ( (err = soundcard_setup_load( cfgfile )) )
return err;
err = soundcard_setup_process( cardno );
soundcard_setup_done();
return err;
}
int main( int argc, char *argv[] )
{
int morehelp;
struct option long_option[] = {
{ "help", 0, NULL, HELPID_HELP },
{ "file", 1, NULL, HELPID_FILE },
{ "debug", 0, NULL, HELPID_DEBUG },
{ "version", 0, NULL, HELPID_VERSION },
{ NULL, 0, NULL, 0 },
};
morehelp = 0;
while ( 1 ) {
int c;
if ( ( c = getopt_long( argc, argv, "hf:dv", long_option, NULL ) ) < 0 ) break;
switch ( c ) {
case 'h':
case HELPID_HELP:
morehelp++;
break;
case 'f':
case HELPID_FILE:
strncpy( cfgfile, optarg, sizeof( cfgfile ) - 1 );
cfgfile[ sizeof( cfgfile ) - 1 ] = 0;
break;
case 'd':
case HELPID_DEBUG:
debugflag = 1;
break;
case 'v':
case HELPID_VERSION:
printf( "alsactl version " VERSION "\n" );
return 0;
default:
fprintf( stderr, "\07Invalid switch or option needs an argument.\n" );
morehelp++;
}
}
if ( morehelp ) {
help();
return 0;
}
if ( argc - optind <= 0 ) {
fprintf( stderr, "alsactl: Specify command...\n" );
return 1;
}
if ( !strcmp( argv[ optind ], "store" ) ) {
return store_setup( argc - optind > 1 ? argv[ optind + 1 ] : NULL );
} else if ( !strcmp( argv[ optind ], "restore" ) ) {
return restore_setup( argc - optind > 1 ? argv[ optind + 1 ] : NULL );
}
return 0;
}

115
alsactl/alsactl.h Normal file
View file

@ -0,0 +1,115 @@
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1997 by Perex, APS, University of South Bohemia
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/asoundlib.h>
#define VERSION "1.00a"
#define ALSACTL_FILE "/etc/asound.conf"
extern int debugflag;
extern void error( const char *fmt, ... );
struct ctl_switch {
int no;
int change;
snd_ctl_switch_t s;
struct ctl_switch *next;
};
struct ctl {
snd_ctl_hw_info_t hwinfo;
struct ctl_switch *switches;
};
struct mixer_channel {
int no;
int change;
snd_mixer_channel_info_t i;
snd_mixer_channel_t c;
struct mixer_channel *next;
};
struct mixer_switch {
int no;
int change;
snd_mixer_switch_t s;
struct mixer_switch *next;
};
struct mixer {
int no;
snd_mixer_info_t info;
struct mixer_channel *channels;
struct mixer_switch *switches;
struct mixer *next;
};
struct pcm_switch {
int no;
int change;
snd_pcm_switch_t s;
struct pcm_switch *next;
};
struct pcm {
int no;
snd_pcm_info_t info;
struct pcm_switch *pswitches;
struct pcm_switch *rswitches;
struct pcm *next;
};
struct rawmidi_switch {
int no;
int change;
snd_rawmidi_switch_t s;
struct rawmidi_switch *next;
};
struct rawmidi {
int no;
snd_rawmidi_info_t info;
struct rawmidi_switch *iswitches;
struct rawmidi_switch *oswitches;
struct rawmidi *next;
};
struct soundcard {
int no; /* card number */
struct ctl control;
struct mixer *mixers;
struct pcm *pcms;
struct rawmidi *rawmidis;
struct soundcard *next;
};
extern struct soundcard *soundcards;
void soundcard_setup_init( void );
void soundcard_setup_done( void );
int soundcard_setup_load( const char *filename );
int soundcard_setup_write( const char *filename );
int soundcard_setup_collect( int cardno );
int soundcard_setup_process( int cardno );

115
alsactl/alsactl_lexer.l Normal file
View file

@ -0,0 +1,115 @@
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1998 by Perex, APS, University of South Bohemia
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
%{
#include "alsactl.h"
#include "alsactl_parser.h"
#define YY_NO_UNPUT
#undef YY_CDECL
#define YY_CDECL int YY_PROTO(yylex( void ));
int linecount;
%}
%%
/* special characters */
"{"|"}" return yytext[0];
"("|")" return yytext[0];
")"[ \t]*"{" return L_DOUBLE1;
"," return yytext[0];
/* tokens */
soundcard return L_SOUNDCARD;
control return L_CONTROL;
mixer return L_MIXER;
channel return L_CHANNEL;
stereo return L_STEREO;
mono return L_MONO;
switch return L_SWITCH;
rawdata return L_RAWDATA;
pcm return L_PCM;
rawmidi return L_RAWMIDI;
playback return L_PLAYBACK;
record return L_RECORD;
output return L_OUTPUT;
input return L_INPUT;
/* boolean */
false|off|no return L_FALSE;
true|on|yes return L_TRUE;
/* integers */
[0-9]+ { yylval.i_value = atoi( yytext ); return L_INTEGER; }
"!"[0-9]+ { yylval.i_value = atoi( yytext ); yylval.i_value |= 0x80000000; return L_INTEGER; }
0x[0-9a-f]+ { char *end;
yylval.i_value = strtol( yytext, &end, 0 );
return L_INTEGER; }
/* byte array */
"@"([0-9a-f]{2}:){0,31}([0-9a-f]{2})"@" {
char *p = yytext + 1, x[3];
unsigned char *d;
int val;
yylval.a_value = d = (unsigned char *)malloc( 32 );
while ( p ) {
strncpy( x, p, 2 ); x[2] = '\0';
sscanf( x, "%02x", &val );
*d++ = val;
}
return L_BYTEARRAY; }
/* strings */
\"[^\"]*\" { yytext[ strlen( yytext ) - 1 ] = 0;
yylval.s_value = strdup( &yytext[ 1 ] );
return L_STRING; }
\'[^\']*\' { yytext[ strlen( yytext ) - 1 ] = 0;
yylval.s_value = strdup( &yytext[ 1 ] );
return L_STRING; }
[a-z0-9/\~@-_\+=:\.]+ { yylval.s_value = strdup( yytext );
return L_STRING; }
$[a-z0-9/\~@-_\+=:\.]+ { yylval.s_value = strdup( getenv( &yytext[ 1 ] ) );
return L_STRING; }
/* comments & whitespaces */
[#\;][^\n]*\n { linecount++; }
[ \t]+ ;
\n { linecount++; }
. fprintf( stderr, "alsactl: discarding char '%c' - line %i\n", yytext[0], linecount + 1 );
%%
#ifndef yywrap
int yywrap(void) /* do this avoid to do -lfl */
{
return 1;
}
#endif

436
alsactl/alsactl_parser.y Normal file
View file

@ -0,0 +1,436 @@
%{
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1998 by Perex, APS, University of South Bohemia
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "alsactl.h"
#include <stdarg.h>
/* insgus_lexer.c */
int yylex( void );
extern char cfgfile[];
extern int linecount;
extern FILE *yyin;
/* local functions */
static void yyerror( char *, ... );
static void select_soundcard( char *name );
static void select_mixer( char *name );
static void select_pcm( char *name );
static void select_rawmidi( char *name );
static void select_mixer_channel( char *name );
static void set_mixer_channel( int left, int right );
static void set_mixer_channel_record( void );
static void set_mixer_channel_end( void );
#define SWITCH_CONTROL 0
#define SWITCH_MIXER 1
#define SWITCH_PCM 2
#define SWITCH_RAWMIDI 3
static void select_control_switch( char *name );
static void select_mixer_switch( char *name );
static void select_pcm_playback_switch( char *name );
static void select_pcm_record_switch( char *name );
static void select_rawmidi_output_switch( char *name );
static void select_rawmidi_input_switch( char *name );
static void set_switch_boolean( int val );
static void set_switch_integer( int val );
/* local variables */
static struct soundcard *Xsoundcard = NULL;
static struct mixer *Xmixer = NULL;
static struct pcm *Xpcm = NULL;
static struct rawmidi *Xrawmidi = NULL;
static struct mixer_channel *Xmixerchannel = NULL;
static unsigned int Xmixerchannelflags = 0;
static int Xswitchtype = SWITCH_CONTROL;
static int *Xswitchchange = NULL;
static void *Xswitch = NULL;
%}
%start lines
%union {
int b_value;
int i_value;
char *s_value;
unsigned char *a_value;
};
%token <b_value> L_TRUE L_FALSE
%token <i_value> L_INTEGER
%token <s_value> L_STRING
%token <a_value> L_BYTEARRAY
/* types */
%token L_INTEGER L_STRING
/* boolean */
%token L_FALSE L_TRUE
/* misc */
%token L_DOUBLE1
/* other keywords */
%token L_SOUNDCARD L_MIXER L_CHANNEL L_STEREO L_MONO L_SWITCH L_RAWDATA
%token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_OUTPUT L_INPUT
%type <b_value> boolean
%type <i_value> integer
%type <s_value> string
%type <a_value> bytearray
%%
lines : line
| lines line
;
line : L_SOUNDCARD '(' string { select_soundcard( $3 ); } L_DOUBLE1 soundcards { select_soundcard( NULL ); } '}'
| error { yyerror( "unknown keyword in top level" ); }
;
soundcards : soundcard
| soundcards soundcard
;
soundcard : L_CONTROL '{' controls '}'
| L_MIXER '(' string { select_mixer( $3 ); } L_DOUBLE1 mixers { select_mixer( NULL ); } '}'
| L_PCM '(' string { select_pcm( $3 ); } L_DOUBLE1 pcms { select_pcm( NULL ); } '}'
| L_RAWMIDI '(' string { select_rawmidi( $3 ); } L_DOUBLE1 rawmidis { select_rawmidi( NULL ); } '}'
| error { yyerror( "unknown keyword in soundcard{} level" ); }
;
controls : control
| controls control
;
control : L_SWITCH '(' string { select_control_switch( $3 ); } ',' switches ')' { select_control_switch( NULL ); }
| error { yyerror( "unknown keyword in control{} level" ); }
;
mixers : mixer
| mixers mixer
;
mixer : L_CHANNEL '(' string { select_mixer_channel( $3 ); } ',' mvalues ')' { set_mixer_channel_end(); select_mixer_channel( NULL ); }
| L_SWITCH '(' string { select_mixer_switch( $3 ); } ',' switches ')' { select_mixer_switch( NULL ); }
| error { yyerror( "unknown keyword in mixer{} level" ); }
;
mvalues : mvalue
| mvalues mvalue
;
mvalue : L_STEREO '(' integer ',' integer ')' { set_mixer_channel( $3, $5 ); }
| L_MONO '(' integer ')' { set_mixer_channel( $3, $3 ); }
| L_RECORD { set_mixer_channel_record(); }
| error { yyerror( "unknown keyword in mixer channel{} level" ); }
;
pcms : pcm
| pcms pcm
;
pcm : L_PLAYBACK '{' playbacks '}'
| L_RECORD '{' records '}'
| error { yyerror( "unknown keyword in pcm{} section" ); }
;
playbacks : playback
| playbacks playback
;
playback : L_SWITCH '(' string { select_pcm_playback_switch( $3 ); } ',' switches ')' { select_pcm_playback_switch( NULL ); }
| error { yyerror( "unknown keyword in playback{} section" ); }
;
records : record
| records record
;
record : L_SWITCH '(' string { select_pcm_record_switch( $3 ); } ',' switches ')' { select_pcm_record_switch( NULL ); }
| error { yyerror( "unknown keyword in record{} section" ); }
;
rawmidis : rawmidi
| rawmidis rawmidi
;
rawmidi : L_INPUT '{' inputs '}'
| L_OUTPUT '{' outputs '}'
;
inputs : input
| inputs input
;
input : L_SWITCH '(' string { select_rawmidi_input_switch( $3 ); } ',' switches ')' { select_rawmidi_input_switch( NULL ); }
| error { yyerror( "unknown keyword in input{} section" ); }
;
outputs : output
| outputs output
;
output : L_SWITCH '(' string { select_rawmidi_output_switch( $3 ); } ',' switches ')' { select_rawmidi_output_switch( NULL ); }
| error { yyerror( "unknown keyword in output{} section" ); }
;
switches : switch
| switches switch
;
switch : L_TRUE { set_switch_boolean( 1 ); }
| L_FALSE { set_switch_boolean( 0 ); }
| L_INTEGER { set_switch_integer( $1 ); }
| error { yyerror( "unknown keyword in switch() data parameter" ); }
;
boolean : L_TRUE { $$ = 1; }
| L_FALSE { $$ = 0; }
| error { yyerror( "unknown boolean value" ); }
;
integer : L_INTEGER { $$ = $1; }
| error { yyerror( "unknown integer value" ); }
;
string : L_STRING { $$ = $1; }
| error { yyerror( "unknown string value" ); }
;
bytearray : L_BYTEARRAY { $$ = $1; }
| error { yyerror( "unknown byte array value" ); }
;
%%
static void yyerror( char *string, ... )
{
char errstr[ 1024 ];
va_list vars;
va_start( vars, string );
vsprintf( errstr, string, vars );
va_end( vars );
error( "Error in configuration file '%s' (line %i): %s", cfgfile, linecount + 1, errstr );
exit( 1 );
}
static void select_soundcard( char *name )
{
struct soundcard *soundcard;
if ( !name ) { Xsoundcard = NULL; return; }
for ( soundcard = soundcards; soundcard; soundcard = soundcard -> next )
if ( !strcmp( soundcard -> control.hwinfo.id, name ) ) {
Xsoundcard = soundcard;
free( name );
return;
}
yyerror( "Cannot find soundcard '%s'...", name );
free( name );
}
static void select_mixer( char *name )
{
struct mixer *mixer;
if ( !name ) { Xmixer = NULL; return; }
for ( mixer = Xsoundcard -> mixers; mixer; mixer = mixer -> next )
if ( !strcmp( mixer -> info.name, name ) ) {
Xmixer = mixer;
free( name );
return;
}
yyerror( "Cannot find mixer '%s' for soundcard '%s'...", name, Xsoundcard -> control.hwinfo.id );
free( name );
}
static void select_pcm( char *name )
{
struct pcm *pcm;
if ( !name ) { Xpcm = NULL; return; }
for ( pcm = Xsoundcard -> pcms; pcm; pcm = pcm -> next )
if ( !strcmp( pcm -> info.name, name ) ) {
Xpcm = pcm;
free( name );
return;
}
yyerror( "Cannot find pcm device '%s' for soundcard '%s'...", name, Xsoundcard -> control.hwinfo.id );
free( name );
}
static void select_rawmidi( char *name )
{
struct rawmidi *rawmidi;
if ( !name ) { Xrawmidi = NULL; return; }
for ( rawmidi = Xsoundcard -> rawmidis; rawmidi; rawmidi = rawmidi -> next )
if ( !strcmp( rawmidi -> info.name, name ) ) {
Xrawmidi = rawmidi;
free( name );
return;
}
yyerror( "Cannot find rawmidi device '%s' for soundcard '%s'...", name, Xsoundcard -> control.hwinfo.id );
free( name );
}
static void select_mixer_channel( char *name )
{
struct mixer_channel *channel;
if ( !name ) { Xmixerchannel = NULL; return; }
for ( channel = Xmixer -> channels; channel; channel = channel -> next )
if ( !strcmp( channel -> i.name, name ) ) {
Xmixerchannel = channel;
free( name );
return;
}
yyerror( "Cannot find mixer channel '%s'...", name );
free( name );
}
static void set_mixer_channel( int left, int right )
{
Xmixerchannelflags = Xmixerchannel -> c.flags &
~(SND_MIXER_FLG_RECORD |
SND_MIXER_FLG_MUTE |
SND_MIXER_FLG_DECIBEL |
SND_MIXER_FLG_FORCE);
if ( left & 0x80000000 ) {
Xmixerchannelflags |= SND_MIXER_FLG_MUTE_LEFT;
left &= 0x7fffffff;
}
if ( right & 0x80000000 ) {
Xmixerchannelflags |= SND_MIXER_FLG_MUTE_RIGHT;
right &= 0x7fffffff;
}
if ( Xmixerchannel -> i.min > left || Xmixerchannel -> i.max < left )
yyerror( "Value out of range (%i-%i)...", Xmixerchannel -> i.min, Xmixerchannel -> i.max );
if ( Xmixerchannel -> i.min > right || Xmixerchannel -> i.max < right )
yyerror( "Value out of range (%i-%i)...", Xmixerchannel -> i.min, Xmixerchannel -> i.max );
if ( Xmixerchannel -> c.left != left || Xmixerchannel -> c.right != right )
Xmixerchannel -> change = 1;
Xmixerchannel -> c.left = left;
Xmixerchannel -> c.right = right;
}
static void set_mixer_channel_record( void )
{
Xmixerchannelflags |= SND_MIXER_FLG_RECORD;
}
static void set_mixer_channel_end( void )
{
if ( Xmixerchannel -> c.flags != Xmixerchannelflags )
Xmixerchannel -> change = 1;
Xmixerchannel -> c.flags = Xmixerchannelflags;
}
#define FIND_SWITCH( xtype, first, name, err ) \
if ( !name ) { Xswitch = Xswitchchange = NULL; return; } \
for ( sw = first; sw; sw = sw -> next ) { \
if ( !strcmp( sw -> s.name, name ) ) { \
Xswitchtype = xtype; \
Xswitchchange = &sw -> change; \
Xswitch = (void *)&sw -> s; \
free( name ); \
return; \
} \
} \
yyerror( "Cannot find " err " switch '%s'...", name ); \
free( name );
static void select_control_switch( char *name )
{
struct ctl_switch *sw;
FIND_SWITCH( SWITCH_CONTROL, Xsoundcard -> control.switches, name, "control" );
}
static void select_mixer_switch( char *name )
{
struct mixer_switch *sw;
FIND_SWITCH( SWITCH_MIXER, Xmixer -> switches, name, "mixer" );
}
static void select_pcm_playback_switch( char *name )
{
struct pcm_switch *sw;
FIND_SWITCH( SWITCH_PCM, Xpcm -> pswitches, name, "pcm playback" );
}
static void select_pcm_record_switch( char *name )
{
struct pcm_switch *sw;
FIND_SWITCH( SWITCH_PCM, Xpcm -> rswitches, name, "pcm record" );
}
static void select_rawmidi_output_switch( char *name )
{
struct rawmidi_switch *sw;
FIND_SWITCH( SWITCH_RAWMIDI, Xrawmidi -> oswitches, name, "rawmidi output" );
}
static void select_rawmidi_input_switch( char *name )
{
struct rawmidi_switch *sw;
FIND_SWITCH( SWITCH_RAWMIDI, Xrawmidi -> iswitches, name, "rawmidi input" );
}
static void set_switch_boolean( int val )
{
/* ok.. this is a little bit wrong, but at these times are all switches same */
snd_ctl_switch_t *sw = (snd_ctl_switch_t *)Xswitch;
unsigned int xx;
if ( sw -> type != SND_CTL_SW_TYPE_BOOLEAN )
yyerror( "Switch '%s' isn't boolean type...", sw -> name );
xx = val & 1;
if ( memcmp( &sw -> value, &xx, sizeof(xx) ) ) *Xswitchchange = 1;
memcpy( &sw -> value, &xx, sizeof(xx) );
}
static void set_switch_integer( int val )
{
/* ok.. this is a little bit wrong, but at these times are all switches same */
snd_ctl_switch_t *sw = (snd_ctl_switch_t *)Xswitch;
unsigned int xx;
if ( sw -> type != SND_CTL_SW_TYPE_BYTE ||
sw -> type != SND_CTL_SW_TYPE_WORD ||
sw -> type != SND_CTL_SW_TYPE_DWORD )
yyerror( "Switch '%s' isn't integer type...", sw -> name );
if ( val < sw -> low || val > sw -> high )
yyerror( "Value for switch '%s' out of range (%i-%i)...\n", sw -> name, sw -> low, sw -> high );
xx = val;
if ( memcmp( &sw -> value, &xx, sizeof(xx) ) ) *Xswitchchange = 1;
memcpy( &sw -> value, &xx, sizeof(xx) );
}

758
alsactl/setup.c Normal file
View file

@ -0,0 +1,758 @@
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1997 by Perex, APS, University of South Bohemia
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "alsactl.h"
#define SND_INTERFACE_CONTROL 0
#define SND_INTERFACE_MIXER 1
#define SND_INTERFACE_PCM 2
#define SND_INTERFACE_RAWMIDI 3
extern int yyparse( void );
extern int linecount;
extern FILE *yyin;
extern int yydebug;
struct soundcard *soundcards = NULL;
/*
* free functions
*/
static void soundcard_ctl_switch_free( struct ctl_switch *first )
{
struct ctl_switch *next;
while ( first ) {
next = first -> next;
free( first );
first = next;
}
}
static void soundcard_mixer_channel_free( struct mixer_channel *first )
{
struct mixer_channel *next;
while ( first ) {
next = first -> next;
free( first );
first = next;
}
}
static void soundcard_mixer_switch_free( struct mixer_switch *first )
{
struct mixer_switch *next;
while ( first ) {
next = first -> next;
free( first );
first = next;
}
}
static void soundcard_mixer_free1( struct mixer *mixer )
{
if ( !mixer ) return;
soundcard_mixer_channel_free( mixer -> channels );
soundcard_mixer_switch_free( mixer -> switches );
free( mixer );
}
static void soundcard_mixer_free( struct mixer *first )
{
struct mixer *next;
while ( first ) {
next = first -> next;
soundcard_mixer_free1( first );
first = next;
}
}
static void soundcard_pcm_switch_free( struct pcm_switch *first )
{
struct pcm_switch *next;
while ( first ) {
next = first -> next;
free( first );
first = next;
}
}
static void soundcard_pcm_free1( struct pcm *pcm )
{
if ( !pcm ) return;
soundcard_pcm_switch_free( pcm -> pswitches );
soundcard_pcm_switch_free( pcm -> rswitches );
free( pcm );
}
static void soundcard_pcm_free( struct pcm *first )
{
struct pcm *next;
while ( first ) {
next = first -> next;
soundcard_pcm_free1( first );
first = next;
}
}
static void soundcard_rawmidi_switch_free( struct rawmidi_switch *first )
{
struct rawmidi_switch *next;
while ( first ) {
next = first -> next;
free( first );
first = next;
}
}
static void soundcard_rawmidi_free1( struct rawmidi *rawmidi )
{
if ( !rawmidi ) return;
soundcard_rawmidi_switch_free( rawmidi -> iswitches );
soundcard_rawmidi_switch_free( rawmidi -> oswitches );
free( rawmidi );
}
static void soundcard_rawmidi_free( struct rawmidi *first )
{
struct rawmidi *next;
while ( first ) {
next = first -> next;
soundcard_rawmidi_free1( first );
first = next;
}
}
static void soundcard_free1( struct soundcard *soundcard )
{
if ( !soundcard ) return;
soundcard_ctl_switch_free( soundcard -> control.switches );
soundcard_mixer_free( soundcard -> mixers );
soundcard_pcm_free( soundcard -> pcms );
soundcard_rawmidi_free( soundcard -> rawmidis );
free( soundcard );
}
static void soundcard_free( struct soundcard *first )
{
struct soundcard *next;
while ( first ) {
next = first -> next;
soundcard_free1( first );
first = next;
}
}
static int soundcard_remove( int cardno )
{
struct soundcard *first, *prev = NULL, *next;
first = soundcards;
while ( first ) {
next = first -> next;
if ( first -> no == cardno ) {
soundcard_free1( first );
if ( !prev ) soundcards = next; else prev -> next = next;
return 0;
}
prev = first;
first = first -> next;
}
return -1;
}
/*
* exported functions
*/
void soundcard_setup_init( void )
{
soundcards = NULL;
}
void soundcard_setup_done( void )
{
soundcard_free( soundcards ); soundcards = NULL;
}
int soundcard_setup_collect( int cardno )
{
void *handle, *mhandle;
struct soundcard *card, *first, *prev;
int err, idx, count, device;
struct ctl_switch *ctl, *ctlprev;
struct mixer *mixer, *mixerprev;
struct mixer_switch *mixsw, *mixswprev;
struct mixer_channel *mixerchannel, *mixerchannelprev;
struct pcm *pcm, *pcmprev;
struct pcm_switch *pcmsw, *pcmswprev;
struct rawmidi *rawmidi, *rawmidiprev;
struct rawmidi_switch *rawmidisw, *rawmidiswprev;
soundcard_remove( cardno );
if ( (err = snd_ctl_open( &handle, cardno )) < 0 ) {
error( "SND CTL open error: %s", snd_strerror( err ) );
return 1;
}
/* --- */
card = (struct soundcard *)malloc( sizeof( struct soundcard ) );
if ( !card ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( card, sizeof( struct soundcard ) );
card -> no = cardno;
for ( first = soundcards, prev = NULL; first; prev = first, first = first -> next )
if ( first -> no > cardno ) {
if ( !prev ) {
soundcards = card;
} else {
prev -> next = card;
}
card -> next = first;
break;
}
if ( !soundcards ) soundcards = card;
if ( (err = snd_ctl_hw_info( handle, &card -> control.hwinfo )) < 0 ) {
snd_ctl_close( handle );
error( "SND CTL HW INFO error: %s", snd_strerror( err ) );
return 1;
}
/* --- */
count = snd_ctl_switches( handle );
for ( idx = 0, ctlprev = NULL; idx < count; idx++ ) {
ctl = (struct ctl_switch *)malloc( sizeof( struct ctl_switch ) );
if ( !ctl ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( ctl, sizeof( struct ctl_switch ) );
ctl -> no = idx;
if ( (err = snd_ctl_switch_read( handle, idx, &ctl -> s )) < 0 ) {
free( ctl );
snd_ctl_close( handle );
error( "CTL switch read error (%s) - skipping" );
break;
}
if ( !ctlprev ) {
card -> control.switches = ctl;
} else {
ctlprev -> next = ctl;
}
ctlprev = ctl;
}
/* --- */
for ( device = 0, mixerprev = NULL; device < card -> control.hwinfo.mixerdevs; device++ ) {
mixer = (struct mixer *)malloc( sizeof( struct mixer ) );
if ( !mixer ) {
snd_ctl_close( handle );
error( "malloc problem" );
return 1;
}
bzero( mixer, sizeof( struct mixer ) );
mixer -> no = device;
count = snd_ctl_mixer_switches( handle, device );
for ( idx = 0, mixswprev = NULL; idx < count; idx++ ) {
mixsw = (struct mixer_switch *)malloc( sizeof( struct mixer_switch ) );
if ( !mixsw ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( mixsw, sizeof( struct mixer_switch ) );
mixsw -> no = idx;
if ( (err = snd_ctl_mixer_switch_read( handle, device, idx, &mixsw -> s )) < 0 ) {
free( mixsw );
error( "MIXER switch read error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( !mixswprev ) {
mixer -> switches = mixsw;
} else {
mixswprev -> next = mixsw;
}
mixswprev = mixsw;
}
if ( !mixerprev ) {
card -> mixers = mixer;
} else {
mixerprev -> next = mixer;
}
mixerprev = mixer;
if ( (err = snd_mixer_open( &mhandle, cardno, device )) < 0 ) {
snd_ctl_close( handle );
error( "MIXER open error: %s\n", snd_strerror( err ) );
return 1;
}
if ( (err = snd_mixer_exact_mode( mhandle, 1 )) < 0 ) {
snd_mixer_close( mhandle );
snd_ctl_close( handle );
error( "MIXER exact mode error: %s\n", snd_strerror( err ) );
return 1;
}
if ( (err = snd_mixer_info( mhandle, &mixer -> info )) < 0 ) {
snd_mixer_close( mhandle );
snd_ctl_close( handle );
error( "MIXER info error: %s\n", snd_strerror( err ) );
return 1;
}
count = snd_mixer_channels( mhandle );
for ( idx = 0, mixerchannelprev = NULL; idx < count; idx++ ) {
mixerchannel = (struct mixer_channel *)malloc( sizeof( struct mixer_channel ) );
if ( !mixerchannel ) {
snd_mixer_close( mhandle );
snd_ctl_close( handle );
error( "malloc problem" );
return 1;
}
bzero( mixerchannel, sizeof( struct mixer_channel ) );
mixerchannel -> no = idx;
if ( (err = snd_mixer_channel_info( mhandle, idx, &mixerchannel -> i )) < 0 ) {
free( mixerchannel );
error( "MIXER channel info error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( (err = snd_mixer_channel_read( mhandle, idx, &mixerchannel -> c )) < 0 ) {
free( mixerchannel );
error( "MIXER channel read error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( !mixerchannelprev ) {
mixer -> channels = mixerchannel;
} else {
mixerchannelprev -> next = mixerchannel;
}
mixerchannelprev = mixerchannel;
}
snd_mixer_close( mhandle );
}
/* --- */
for ( device = 0, pcmprev = NULL; device < card -> control.hwinfo.pcmdevs; device++ ) {
pcm = (struct pcm *)malloc( sizeof( struct pcm ) );
if ( !pcm ) {
snd_ctl_close( handle );
error( "malloc problem" );
return 1;
}
bzero( pcm, sizeof( struct pcm ) );
pcm -> no = device;
if ( (err = snd_ctl_pcm_info( handle, device, &pcm -> info )) < 0 ) {
snd_ctl_close( handle );
error( "PCM info error: %s\n", snd_strerror( err ) );
return 1;
}
count = snd_ctl_pcm_playback_switches( handle, device );
for ( idx = 0, pcmswprev = NULL; idx < count; idx++ ) {
pcmsw = (struct pcm_switch *)malloc( sizeof( struct pcm_switch ) );
if ( !pcmsw ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( pcmsw, sizeof( struct mixer_switch ) );
pcmsw -> no = idx;
if ( (err = snd_ctl_pcm_playback_switch_read( handle, device, idx, &pcmsw -> s )) < 0 ) {
free( pcmsw );
error( "PCM playback switch read error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( !pcmswprev ) {
pcm -> pswitches = pcmsw;
} else {
pcmswprev -> next = pcmsw;
}
pcmswprev = pcmsw;
}
count = snd_ctl_pcm_record_switches( handle, device );
for ( idx = 0, pcmswprev = NULL; idx < count; idx++ ) {
pcmsw = (struct pcm_switch *)malloc( sizeof( struct pcm_switch ) );
if ( !pcmsw ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( pcmsw, sizeof( struct mixer_switch ) );
pcmsw -> no = idx;
if ( (err = snd_ctl_pcm_record_switch_read( handle, device, idx, &pcmsw -> s )) < 0 ) {
free( pcmsw );
error( "PCM record switch read error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( !pcmswprev ) {
pcm -> rswitches = pcmsw;
} else {
pcmswprev -> next = pcmsw;
}
pcmswprev = pcmsw;
}
if ( !pcmprev ) {
card -> pcms = pcm;
} else {
pcmprev -> next = pcm;
}
pcmprev = pcm;
}
/* --- */
for ( device = 0, rawmidiprev = NULL; device < card -> control.hwinfo.mididevs; device++ ) {
rawmidi = (struct rawmidi *)malloc( sizeof( struct rawmidi ) );
if ( !rawmidi ) {
snd_ctl_close( handle );
error( "malloc problem" );
return 1;
}
bzero( rawmidi, sizeof( struct rawmidi ) );
rawmidi -> no = device;
if ( (err = snd_ctl_rawmidi_info( handle, device, &rawmidi -> info )) < 0 ) {
snd_ctl_close( handle );
error( "RAWMIDI info error: %s\n", snd_strerror( err ) );
return 1;
}
count = snd_ctl_rawmidi_input_switches( handle, device );
for ( idx = 0, rawmidiswprev = NULL; idx < count; idx++ ) {
rawmidisw = (struct rawmidi_switch *)malloc( sizeof( struct rawmidi_switch ) );
if ( !rawmidisw ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( rawmidisw, sizeof( struct rawmidi_switch ) );
rawmidisw -> no = idx;
if ( (err = snd_ctl_rawmidi_input_switch_read( handle, device, idx, &rawmidisw -> s )) < 0 ) {
free( rawmidisw );
error( "RAWMIDI input switch read error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( !rawmidiswprev ) {
rawmidi -> iswitches = rawmidisw;
} else {
rawmidiswprev -> next = rawmidisw;
}
rawmidiswprev = rawmidisw;
}
count = snd_ctl_rawmidi_output_switches( handle, device );
for ( idx = 0, rawmidiswprev = NULL; idx < count; idx++ ) {
rawmidisw = (struct rawmidi_switch *)malloc( sizeof( struct rawmidi_switch ) );
if ( !rawmidisw ) {
snd_ctl_close( handle );
error( "malloc error" );
return 1;
}
bzero( rawmidisw, sizeof( struct rawmidi_switch ) );
rawmidisw -> no = idx;
if ( (err = snd_ctl_rawmidi_output_switch_read( handle, device, idx, &rawmidisw -> s )) < 0 ) {
free( rawmidisw );
error( "RAWMIDI output switch read error (%s) - skipping", snd_strerror( err ) );
break;
}
if ( !rawmidiswprev ) {
rawmidi -> oswitches = rawmidisw;
} else {
rawmidiswprev -> next = rawmidisw;
}
rawmidiswprev = rawmidisw;
}
if ( !rawmidiprev ) {
card -> rawmidis = rawmidi;
} else {
rawmidiprev -> next = rawmidi;
}
rawmidiprev = rawmidi;
}
/* --- */
snd_ctl_close( handle );
return 0;
}
int soundcard_setup_load( const char *cfgfile )
{
extern int yyparse( void );
extern int linecount;
extern FILE *yyin;
extern int yydebug;
int xtry;
#ifdef YYDEBUG
yydebug = 1;
#endif
if ( debugflag )
printf( "cfgfile = '%s'\n", cfgfile );
if ( ( yyin = fopen( cfgfile, "r" ) ) == NULL ) {
error( "Cannot open configuration file '%s'...", cfgfile );
return 1;
}
linecount = 0;
xtry = yyparse();
fclose( yyin );
if ( debugflag )
printf( "Config ok..\n" );
if ( xtry )
error( "Ignored error in configuration file '%s'...", cfgfile );
return 0;
}
static void soundcard_setup_write_switch( FILE *out, int interface, const unsigned char *name, unsigned int type, unsigned int low, unsigned int high, void *data )
{
struct data {
unsigned int enable;
unsigned char data8[32];
unsigned short data16[16];
unsigned int data32[8];
};
char *s, v[16];
struct data *pdata = (struct data *)data;
int idx;
v[0] = '\0';
switch ( type ) {
case SND_CTL_SW_TYPE_BOOLEAN: s = "bool"; strcpy( v, pdata -> enable ? "true" : "false" ); break;
case SND_CTL_SW_TYPE_BYTE: s = "byte"; sprintf( v, "%ui", (unsigned int)pdata -> data8[0] ); break;
case SND_CTL_SW_TYPE_WORD: s = "word"; sprintf( v, "%ui", (unsigned int)pdata -> data16[0] ); break;
case SND_CTL_SW_TYPE_DWORD: s = "dword"; sprintf( v, "%ui", pdata -> data32[0] ); break;
case SND_CTL_SW_TYPE_USER: s = "user"; break;
default:
s = "unknown";
}
fprintf( out, " ; Type is '%s'.\n", s );
if ( low != 0 || high != 0 )
fprintf( out, " ; Accepted switch range is from %ui to %ui.\n", low, high );
fprintf( out, " switch( \"%s\", %s", name, v );
if ( type < 0 || type > SND_CTL_SW_TYPE_DWORD ) {
/* TODO: some well known types should be verbose */
fprintf( out, " rawdata( " );
for ( idx = 0; idx < 31; idx++ ) {
fprintf( out, "@%02x:", pdata -> data8[idx] );
}
fprintf( out, "%02x@ )\n", pdata -> data8[31] );
}
fprintf( out, " )\n" );
}
static void soundcard_setup_write_mixer_channel( FILE *out, snd_mixer_channel_info_t *info, snd_mixer_channel_t *channel )
{
fprintf( out, " ; Capabilities:%s%s%s%s%s%s.\n",
info -> caps & SND_MIXER_CINFO_CAP_RECORD ? " record" : "",
info -> caps & SND_MIXER_CINFO_CAP_STEREO ? " stereo" : "",
info -> caps & SND_MIXER_CINFO_CAP_HWMUTE ? " hardware-mute" : "",
info -> caps & SND_MIXER_CINFO_CAP_MONOMUTE ? " mono-mute" : "",
info -> caps & SND_MIXER_CINFO_CAP_DIGITAL ? " digital" : "",
info -> caps & SND_MIXER_CINFO_CAP_INPUT ? " external-input" : "" );
fprintf( out, " ; Accepted channel range is from %i to %i.\n", info -> min, info -> max );
fprintf( out, " channel( \"%s\", ", info -> name );
if ( info -> caps & SND_MIXER_CINFO_CAP_STEREO ) {
fprintf( out, "stereo( %s%i, %s%i )", channel -> flags & SND_MIXER_FLG_MUTE_LEFT ? "!" : "", channel -> left, channel -> flags & SND_MIXER_FLG_MUTE_RIGHT ? "!" : "", channel -> right );
} else {
fprintf( out, "mono( %s%i )", channel -> flags & SND_MIXER_FLG_MUTE ? "!" : "", channel -> left );
}
if ( channel -> flags & SND_MIXER_FLG_RECORD )
fprintf( out, " record" );
fprintf( out, " )\n" );
}
int soundcard_setup_write( const char *cfgfile )
{
FILE *out;
struct soundcard *first;
struct ctl_switch *ctlsw;
struct mixer *mixer;
struct mixer_switch *mixersw;
struct mixer_channel *mixerchannel;
struct pcm *pcm;
struct pcm_switch *pcmsw;
struct rawmidi *rawmidi;
struct rawmidi_switch *rawmidisw;
if ( (out = fopen( cfgfile, "w+" )) == NULL ) {
error( "Cannot open file '%s' for writing...\n", cfgfile );
return 1;
}
fprintf( out, "# ALSA driver configuration\n" );
fprintf( out, "# Generated by alsactl\n" );
fprintf( out, "\n" );
for ( first = soundcards; first; first = first -> next ) {
fprintf( out, "soundcard( \"%s\" ) {\n", first -> control.hwinfo.id );
if ( first -> control.switches ) {
fprintf( out, " control {\n" );
for ( ctlsw = first -> control.switches; ctlsw; ctlsw = ctlsw -> next )
soundcard_setup_write_switch( out, SND_INTERFACE_CONTROL, ctlsw -> s.name, ctlsw -> s.type, ctlsw -> s.low, ctlsw -> s.high, (void *)&ctlsw -> s.value );
fprintf( out, " }\n" );
}
for ( mixer = first -> mixers; mixer; mixer = mixer -> next ) {
fprintf( out, " mixer( \"%s\" ) {\n", mixer -> info.name );
for ( mixerchannel = mixer -> channels; mixerchannel; mixerchannel = mixerchannel -> next )
soundcard_setup_write_mixer_channel( out, &mixerchannel -> i, &mixerchannel -> c );
for ( mixersw = mixer -> switches; mixersw; mixersw = mixersw -> next )
soundcard_setup_write_switch( out, SND_INTERFACE_MIXER, mixersw -> s.name, mixersw -> s.type, mixersw -> s.low, mixersw -> s.high, (void *)&mixersw -> s.value );
fprintf( out, " }\n" );
}
for ( pcm = first -> pcms; pcm; pcm = pcm -> next ) {
if ( !pcm -> pswitches && !pcm -> rswitches ) continue;
fprintf( out, " pcm( \"%s\" ) {\n", pcm -> info.name );
if ( pcm -> pswitches ) {
fprintf( out, " playback {" );
for ( pcmsw = pcm -> pswitches; pcmsw; pcmsw = pcmsw -> next )
soundcard_setup_write_switch( out, SND_INTERFACE_PCM, pcmsw -> s.name, pcmsw -> s.type, pcmsw -> s.low, pcmsw -> s.high, (void *)&pcmsw -> s.value );
fprintf( out, " }\n" );
}
if ( pcm -> rswitches ) {
fprintf( out, " record {" );
for ( pcmsw = pcm -> pswitches; pcmsw; pcmsw = pcmsw -> next )
soundcard_setup_write_switch( out, SND_INTERFACE_PCM, pcmsw -> s.name, pcmsw -> s.type, pcmsw -> s.low, pcmsw -> s.high, (void *)&pcmsw -> s.value );
fprintf( out, " }\n" );
}
fprintf( out, " }\n" );
}
for ( rawmidi = first -> rawmidis; rawmidi; rawmidi = rawmidi -> next ) {
if ( !rawmidi -> oswitches && !rawmidi -> iswitches ) continue;
fprintf( out, " rawmidi( \"%s\" ) {\n", rawmidi -> info.name );
if ( rawmidi -> oswitches ) {
fprintf( out, " output {" );
for ( rawmidisw = rawmidi -> oswitches; rawmidisw; rawmidisw = rawmidisw -> next )
soundcard_setup_write_switch( out, SND_INTERFACE_RAWMIDI, rawmidisw -> s.name, rawmidisw -> s.type, rawmidisw -> s.low, rawmidisw -> s.high, (void *)&rawmidisw -> s.value );
fprintf( out, " }\n" );
}
if ( rawmidi -> iswitches ) {
fprintf( out, " input {" );
for ( rawmidisw = rawmidi -> iswitches; rawmidisw; rawmidisw = rawmidisw -> next )
soundcard_setup_write_switch( out, SND_INTERFACE_RAWMIDI, rawmidisw -> s.name, rawmidisw -> s.type, rawmidisw -> s.low, rawmidisw -> s.high, (void *)&rawmidisw -> s.value );
fprintf( out, " }\n" );
}
fprintf( out, " }\n" );
}
fprintf( out, "}\n%s", first -> next ? "\n" : "" );
}
fclose( out );
return 0;
}
static int soundcard_open_ctl( void **ctlhandle, struct soundcard *soundcard )
{
int err;
if ( *ctlhandle ) return 0;
if ( (err = snd_ctl_open( ctlhandle, soundcard -> no )) < 0 ) {
error( "Cannot open control interface for soundcard #%i.", soundcard -> no + 1 );
return 1;
}
return 0;
}
static int soundcard_open_mix( void **mixhandle, struct soundcard *soundcard, struct mixer *mixer )
{
int err;
if ( *mixhandle ) return 0;
if ( (err = snd_mixer_open( mixhandle, soundcard -> no, mixer -> no )) < 0 ) {
error( "Cannot open mixer interface for soundcard #%i.", soundcard -> no + 1 );
return 1;
}
if ( (err = snd_mixer_exact_mode( *mixhandle, 1 )) < 0 ) {
error( "Cannot setup exact mode for mixer #%i/#%i: %s", soundcard -> no + 1, mixer -> no, snd_strerror( err ) );
return 1;
}
return 0;
}
int soundcard_setup_process( int cardno )
{
int err;
void *ctlhandle = NULL;
void *mixhandle = NULL;
struct soundcard *soundcard;
struct ctl_switch *ctlsw;
struct mixer *mixer;
struct mixer_channel *channel;
struct mixer_switch *mixersw;
struct pcm *pcm;
struct pcm_switch *pcmsw;
struct rawmidi *rawmidi;
struct rawmidi_switch *rawmidisw;
for ( soundcard = soundcards; soundcard; soundcard = soundcard -> next ) {
if ( cardno >= 0 && soundcard -> no != cardno ) continue;
for ( ctlsw = soundcard -> control.switches; ctlsw; ctlsw = ctlsw -> next ) {
if ( ctlsw -> change )
if ( !soundcard_open_ctl( &ctlhandle, soundcard ) ) {
if ( (err = snd_ctl_switch_write( ctlhandle, ctlsw -> no, &ctlsw -> s )) < 0 )
error( "Control switch '%s' write error: %s", ctlsw -> s.name, snd_strerror( err ) );
}
}
for ( mixer = soundcard -> mixers; mixer; mixer = mixer -> next ) {
for ( channel = mixer -> channels; channel; channel = channel -> next )
if ( channel -> change )
if ( !soundcard_open_mix( &mixhandle, soundcard, mixer ) ) {
if ( (err = snd_mixer_channel_write( mixhandle, channel -> no, &channel -> c )) < 0 )
error( "Mixer channel '%s' write error: %s", channel -> i.name, snd_strerror( err ) );
}
if ( mixhandle ) { snd_mixer_close( mixhandle ); mixhandle = NULL; }
for ( mixersw = mixer -> switches; mixersw; mixersw = mixersw -> next )
if ( mixersw -> change )
if ( !soundcard_open_ctl( &ctlhandle, soundcard ) ) {
if ( (err = snd_ctl_mixer_switch_write( ctlhandle, mixer -> no, mixersw -> no, &mixersw -> s )) < 0 )
error( "Mixer switch '%s' write error: %s", mixersw -> s.name, snd_strerror( err ) );
}
}
for ( pcm = soundcard -> pcms; pcm; pcm = pcm -> next ) {
for ( pcmsw = pcm -> pswitches; pcmsw; pcmsw = pcmsw -> next ) {
if ( pcmsw -> change )
if ( !soundcard_open_ctl( &ctlhandle, soundcard ) ) {
if ( (err = snd_ctl_pcm_playback_switch_write( ctlhandle, pcm -> no, pcmsw -> no, &pcmsw -> s )) < 0 )
error( "PCM playback switch '%s' write error: %s", pcmsw -> s.name, snd_strerror( err ) );
}
}
for ( pcmsw = pcm -> rswitches; pcmsw; pcmsw = pcmsw -> next ) {
if ( pcmsw -> change )
if ( !soundcard_open_ctl( &ctlhandle, soundcard ) ) {
if ( (err = snd_ctl_pcm_playback_switch_write( ctlhandle, pcm -> no, pcmsw -> no, &pcmsw -> s )) < 0 )
error( "PCM record switch '%s' write error: %s", pcmsw -> s.name, snd_strerror( err ) );
}
}
}
for ( rawmidi = soundcard -> rawmidis; rawmidi; rawmidi = rawmidi -> next ) {
for ( rawmidisw = rawmidi -> oswitches; rawmidisw; rawmidisw = rawmidisw -> next ) {
if ( rawmidisw -> change )
if ( !soundcard_open_ctl( &ctlhandle, soundcard ) ) {
if ( (err = snd_ctl_rawmidi_output_switch_write( ctlhandle, rawmidi -> no, rawmidisw -> no, &rawmidisw -> s )) < 0 )
error( "RAWMIDI output switch '%s' write error: %s", rawmidisw -> s.name, snd_strerror( err ) );
}
}
for ( rawmidisw = rawmidi -> iswitches; rawmidisw; rawmidisw = rawmidisw -> next ) {
if ( rawmidisw -> change )
if ( !soundcard_open_ctl( &ctlhandle, soundcard ) ) {
if ( (err = snd_ctl_rawmidi_output_switch_write( ctlhandle, rawmidi -> no, rawmidisw -> no, &rawmidisw -> s )) < 0 )
error( "RAWMIDI input switch '%s' write error: %s", rawmidisw -> s.name, snd_strerror( err ) );
}
}
}
}
if ( ctlhandle ) snd_ctl_close( ctlhandle );
return 1;
}