alsa-utils/alsactl/alsactl_parser.y
1998-10-29 22:45:59 +00:00

437 lines
12 KiB
Text

%{
/*
* 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) );
}