OSDN Git Service

Initial version of alsactl...
authorJaroslav Kysela <perex@perex.cz>
Thu, 29 Oct 1998 22:45:59 +0000 (22:45 +0000)
committerJaroslav Kysela <perex@perex.cz>
Thu, 29 Oct 1998 22:45:59 +0000 (22:45 +0000)
alsactl/Makefile [new file with mode: 0644]
alsactl/alsactl.c [new file with mode: 0644]
alsactl/alsactl.h [new file with mode: 0644]
alsactl/alsactl_lexer.l [new file with mode: 0644]
alsactl/alsactl_parser.y [new file with mode: 0644]
alsactl/setup.c [new file with mode: 0644]

diff --git a/alsactl/Makefile b/alsactl/Makefile
new file mode 100644 (file)
index 0000000..75aef5f
--- /dev/null
@@ -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
diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c
new file mode 100644 (file)
index 0000000..5341861
--- /dev/null
@@ -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;
+}
diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h
new file mode 100644 (file)
index 0000000..f876feb
--- /dev/null
@@ -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 );
diff --git a/alsactl/alsactl_lexer.l b/alsactl/alsactl_lexer.l
new file mode 100644 (file)
index 0000000..3283b3e
--- /dev/null
@@ -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 (file)
index 0000000..79a41a3
--- /dev/null
@@ -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) );
+}
diff --git a/alsactl/setup.c b/alsactl/setup.c
new file mode 100644 (file)
index 0000000..abebdb4
--- /dev/null
@@ -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;
+}