From 72ff837506e54449d2f739f14182b42bef847ad1 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 29 Oct 1998 22:45:59 +0000 Subject: [PATCH] Initial version of alsactl... --- alsactl/Makefile | 45 +++ alsactl/alsactl.c | 195 ++++++++++++ alsactl/alsactl.h | 115 +++++++ alsactl/alsactl_lexer.l | 115 +++++++ alsactl/alsactl_parser.y | 436 +++++++++++++++++++++++++++ alsactl/setup.c | 758 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1664 insertions(+) create mode 100644 alsactl/Makefile create mode 100644 alsactl/alsactl.c create mode 100644 alsactl/alsactl.h create mode 100644 alsactl/alsactl_lexer.l create mode 100644 alsactl/alsactl_parser.y create mode 100644 alsactl/setup.c diff --git a/alsactl/Makefile b/alsactl/Makefile new file mode 100644 index 0000000..75aef5f --- /dev/null +++ b/alsactl/Makefile @@ -0,0 +1,45 @@ +# +# Makefile for alsactl program +# Copyright (c) 1994-98 by Jaroslav Kysela +# + +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 diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c new file mode 100644 index 0000000..5341861 --- /dev/null +++ b/alsactl/alsactl.c @@ -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 +#include + +#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 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 store current driver setup for one or each soundcards\n" ); + printf( " to configuration file\n" ); + printf( " restore 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; +} diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h new file mode 100644 index 0000000..f876feb --- /dev/null +++ b/alsactl/alsactl.h @@ -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 +#include +#include +#include + +#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 ); diff --git a/alsactl/alsactl_lexer.l b/alsactl/alsactl_lexer.l new file mode 100644 index 0000000..3283b3e --- /dev/null +++ b/alsactl/alsactl_lexer.l @@ -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 diff --git a/alsactl/alsactl_parser.y b/alsactl/alsactl_parser.y new file mode 100644 index 0000000..79a41a3 --- /dev/null +++ b/alsactl/alsactl_parser.y @@ -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 + + /* 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 L_TRUE L_FALSE +%token L_INTEGER +%token L_STRING +%token 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 boolean +%type integer +%type string +%type 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) ); +} diff --git a/alsactl/setup.c b/alsactl/setup.c new file mode 100644 index 0000000..abebdb4 --- /dev/null +++ b/alsactl/setup.c @@ -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; +} -- 2.11.0