Rewritten alsactl

This commit is contained in:
Abramo Bagnara 2000-08-25 14:34:26 +00:00
parent 6add1cdfb5
commit 8c4e1aa769
7 changed files with 929 additions and 1281 deletions

View file

@ -1,10 +1,4 @@
sbin_PROGRAMS=alsactl
noinst_HEADERS=alsactl.h
man_MANS=alsactl.1
alsactl_SOURCES=alsactl.c alsactl_parser.y setup.c merge.c alsactl_lexer.l
YFLAGS=-d
# lexer / parser debug
#CFLAGS=-pipe -g -DYYDEBUG
#LFLAGS=-d
alsactl_SOURCES=alsactl.c

File diff suppressed because it is too large Load diff

View file

@ -1,70 +0,0 @@
/*
* 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 <errno.h>
#include <sys/asoundlib.h>
#define ALSACTL_FILE "/etc/asound.conf"
#define LEFT 1
#define RIGHT 2
#define OUTPUT 0
#define INPUT 1
extern int debugflag;
extern void error(const char *fmt,...);
struct ctl_control {
int change;
snd_control_type_t type;
snd_control_info_t info;
snd_control_t c;
struct ctl_control *next;
};
struct ctl {
snd_ctl_hw_info_t hwinfo;
struct ctl_control *controls;
};
struct soundcard {
int no; /* card number */
struct ctl control;
struct soundcard *next;
};
extern struct soundcard *soundcards;
extern struct soundcard *rsoundcards; /* read soundcards */
void soundcard_setup_init(void);
void soundcard_setup_done(void);
int soundcard_setup_load(const char *filename, int skip);
int soundcard_setup_write(const char *filename, int cardno);
int soundcard_setup_collect_controls(int cardno);
int soundcard_setup_merge_controls(int cardno);
int soundcard_setup_process_controls(int cardno);
char *control_id(snd_control_id_t *id);

View file

@ -1,114 +0,0 @@
/*
* 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"
struct bytearray {
unsigned char *data;
size_t datalen;
};
#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];
"["|"]" return yytext[0];
")"[ \t]*"{" return L_DOUBLE1;
"," return yytext[0];
"=" return yytext[0];
/* tokens */
soundcard return L_SOUNDCARD;
control return L_CONTROL;
global return L_GLOBAL;
hwdep return L_HWDEP;
mixer return L_MIXER;
pcm return L_PCM;
rawmidi return L_RAWMIDI;
timer return L_TIMER;
sequencer return L_SEQUENCER;
ident return L_IDENT;
iface return L_IFACE;
name return L_NAME;
device return L_DEVICE;
subdevice return L_SUBDEVICE;
index return L_INDEX;
bool return L_BOOL;
int return L_INT;
enum return L_ENUM;
byte return L_BYTE;
/* boolean */
false|off|no return L_FALSE;
true|on|yes return L_TRUE;
/* integers */
[0-9]+ { yylval.i_value = strtol(yytext, (char **)NULL, 10); return L_INTEGER; }
0x[0-9a-f]+ { yylval.i_value = strtol(yytext, (char **)NULL, 0); return L_INTEGER; }
/* 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/\~@-Za-z_]+ { yylval.s_value = strdup(yytext);
return L_STRING; }
$[a-z0-9/\~@-Za-z_]+ { 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

View file

@ -1,319 +0,0 @@
%{
/*
* 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;
/* structure for byte arrays */
struct bytearray {
unsigned char *data;
size_t datalen;
};
/* local functions */
static void yyerror(char *, ...);
static void build_soundcard(char *name);
static void build_control_begin(void);
static void build_control_end(void);
static void set_control_iface(int iface);
static void set_control_device(int dev);
static void set_control_subdevice(int subdev);
static void set_control_name(char *name);
static void set_control_index(int idx);
static void set_control_type(snd_control_type_t type);
static void set_control_boolean(int val);
static void set_control_integer(long val);
/* local variables */
static struct soundcard *Xsoundcard = NULL;
static struct ctl_control *Xcontrol = NULL;
static int Xposition = 0;
static snd_control_type_t Xtype = SND_CONTROL_TYPE_NONE;
%}
%start lines
%union {
int b_value;
long i_value;
char *s_value;
struct bytearray 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_CONTROL L_RAWDATA
%token L_GLOBAL L_HWDEP L_MIXER L_PCM L_RAWMIDI L_TIMER L_SEQUENCER
%token L_IDENT L_IFACE L_NAME L_DEVICE L_SUBDEVICE L_INDEX
%token L_BOOL L_INT L_ENUM L_BYTE
%type <b_value> boolean
%type <i_value> integer iface
%type <s_value> string
%type <a_value> rawdata
%%
lines : line
| lines line
;
line : L_SOUNDCARD '(' string { build_soundcard($3); }
L_DOUBLE1 soundcards '}' { build_soundcard(NULL); }
| error { yyerror("unknown keyword in top level"); }
;
soundcards :
| soundcards soundcard
;
soundcard : L_CONTROL '(' L_IDENT '=' { build_control_begin(); }
'{' ctlids '}' ',' controls ')' { build_control_end(); }
| error { yyerror("an unknown keyword in the soundcard{} level"); }
;
ctlids : ctlid
| ctlids ',' ctlid
;
ctlid : L_IFACE '=' iface { set_control_iface($3); }
| L_DEVICE '=' integer { set_control_device($3); }
| L_SUBDEVICE '=' integer { set_control_subdevice($3); }
| L_NAME '=' string { set_control_name($3); }
| L_INDEX '=' integer { set_control_index($3); }
| error { yyerror("an unknown keyword in the control ID level"); }
;
controls : control
;
control : L_BOOL '=' { set_control_type(SND_CONTROL_TYPE_BOOLEAN); } '{' datas '}'
| L_INT '=' { set_control_type(SND_CONTROL_TYPE_INTEGER); } '{' datas '}'
| L_ENUM '=' { set_control_type(SND_CONTROL_TYPE_ENUMERATED); } '{' datas '}'
| L_BYTE '=' { set_control_type(SND_CONTROL_TYPE_BYTES); } '{' datas '}'
| error { yyerror( "an unknown keyword in the control() data parameter" ); }
;
datas : data
| datas ',' data
;
data : boolean { set_control_boolean($1); }
| integer { set_control_integer($1); }
| error { yyerror( "an unknown keyword in the control() data argument" ); }
;
iface : L_INTEGER { $$ = $1; }
| L_GLOBAL { $$ = SND_CONTROL_IFACE_CARD; }
| L_HWDEP { $$ = SND_CONTROL_IFACE_HWDEP; }
| L_MIXER { $$ = SND_CONTROL_IFACE_MIXER; }
| L_PCM { $$ = SND_CONTROL_IFACE_PCM; }
| L_RAWMIDI { $$ = SND_CONTROL_IFACE_RAWMIDI; }
| L_TIMER { $$ = SND_CONTROL_IFACE_TIMER; }
| L_SEQUENCER { $$ = SND_CONTROL_IFACE_SEQUENCER; }
| error { yyerror( "an unknown keyword in the interface field"); }
;
boolean : L_TRUE { $$ = 1; }
| L_FALSE { $$ = 0; }
;
integer : L_INTEGER { $$ = $1; }
;
string : L_STRING { $$ = $1; }
;
rawdata : L_RAWDATA '(' L_BYTEARRAY ')' { $$ = $3; }
| L_RAWDATA error { yyerror( "malformed rawdata 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 error_nomem(void)
{
yyerror("No enough memory...\n");
}
static void build_soundcard(char *name)
{
struct soundcard *soundcard;
if (!name) {
Xsoundcard = NULL;
return;
}
Xsoundcard = (struct soundcard *)malloc(sizeof(struct soundcard));
if (!Xsoundcard) {
free(name);
error_nomem();
return;
}
bzero(Xsoundcard, sizeof(*Xsoundcard));
for (soundcard = rsoundcards; soundcard && soundcard->next; soundcard = soundcard->next);
if (soundcard) {
soundcard->next = Xsoundcard;
} else {
rsoundcards = Xsoundcard;
}
strncpy(Xsoundcard->control.hwinfo.id, name, sizeof(Xsoundcard->control.hwinfo.id));
free(name);
}
static void build_control_begin(void)
{
struct ctl_control **first;
struct ctl_control *ctl;
first = &Xsoundcard->control.controls;
Xcontrol = (struct ctl_control *)malloc(sizeof(struct ctl_control));
if (!Xcontrol) {
error_nomem();
return;
}
Xposition = 0;
Xtype = SND_CONTROL_TYPE_NONE;
bzero(Xcontrol, sizeof(*Xcontrol));
for (ctl = *first; ctl && ctl->next; ctl = ctl->next);
if (ctl) {
ctl->next = Xcontrol;
} else {
*first = Xcontrol;
}
}
static void build_control_end(void)
{
Xcontrol = NULL;
}
static void set_control_iface(int iface)
{
Xcontrol->c.id.iface = iface;
}
static void set_control_device(int dev)
{
Xcontrol->c.id.device = dev;
}
static void set_control_subdevice(int subdev)
{
Xcontrol->c.id.subdevice = subdev;
}
static void set_control_name(char *name)
{
if (name == NULL)
return;
strncpy(Xcontrol->c.id.name, name, sizeof(Xcontrol->c.id.name));
free(name);
}
static void set_control_index(int idx)
{
Xcontrol->c.id.index = idx;
}
static void set_control_type(snd_control_type_t type)
{
Xcontrol->type = Xtype = type;
}
static void set_control_boolean(int val)
{
if (Xposition >= 512)
yyerror("Array overflow.");
switch (Xtype) {
case SND_CONTROL_TYPE_BOOLEAN:
Xcontrol->c.value.integer.value[Xposition++] = val ? 1 : 0;
break;
case SND_CONTROL_TYPE_INTEGER:
Xcontrol->c.value.integer.value[Xposition++] = val ? 1 : 0;
break;
case SND_CONTROL_TYPE_ENUMERATED:
Xcontrol->c.value.enumerated.item[Xposition++] = val ? 1 : 0;
break;
case SND_CONTROL_TYPE_BYTES:
Xcontrol->c.value.bytes.data[Xposition++] = val ? 1 : 0;
break;
default: break;
}
}
static void set_control_integer(long val)
{
if (Xposition >= 512)
yyerror("Array overflow.");
switch (Xtype) {
case SND_CONTROL_TYPE_BOOLEAN:
Xcontrol->c.value.integer.value[Xposition++] = val ? 1 : 0;
break;
case SND_CONTROL_TYPE_INTEGER:
Xcontrol->c.value.integer.value[Xposition++] = val;
break;
case SND_CONTROL_TYPE_ENUMERATED:
Xcontrol->c.value.enumerated.item[Xposition++] = (unsigned int)val;
break;
case SND_CONTROL_TYPE_BYTES:
Xcontrol->c.value.bytes.data[Xposition++] = (unsigned char)val;
break;
default: break;
}
}

View file

@ -1,170 +0,0 @@
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
*
*
* 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"
static int merge_one_control(struct ctl_control *cctl, struct ctl_control *uctl, int cardno)
{
int idx;
if (!(cctl->info.access & SND_CONTROL_ACCESS_WRITE))
return 0;
switch (cctl->info.type) {
case SND_CONTROL_TYPE_BOOLEAN:
if (uctl->type != SND_CONTROL_TYPE_BOOLEAN) {
error("A wrong type %i for the control '%s'. The type integer is expected. Skipping...", uctl->type, control_id(&uctl->c.id));
return 1;
}
for (idx = 0; idx < cctl->info.values_count; idx++) {
if (cctl->c.value.integer.value[idx] != uctl->c.value.integer.value[idx]) {
cctl->change = 1;
cctl->c.value.integer.value[idx] = uctl->c.value.integer.value[idx];
}
}
break;
case SND_CONTROL_TYPE_INTEGER:
if (uctl->type != SND_CONTROL_TYPE_INTEGER) {
error("A wrong type %i for the control '%s'. The type integer is expected. Skipping...", uctl->type, control_id(&uctl->c.id));
return 1;
}
for (idx = 0; idx < cctl->info.values_count; idx++) {
if (cctl->info.value.integer.min > uctl->c.value.integer.value[idx] ||
cctl->info.value.integer.max < uctl->c.value.integer.value[idx]) {
error("The value %li for the control '%s' is out of range %i-%i.", uctl->c.value.integer.value[idx], control_id(&uctl->c.id), cctl->info.value.integer.min, cctl->info.value.integer.max);
return 1;
}
if (cctl->c.value.integer.value[idx] != uctl->c.value.integer.value[idx]) {
cctl->change = 1;
cctl->c.value.integer.value[idx] = uctl->c.value.integer.value[idx];
}
}
break;
case SND_CONTROL_TYPE_ENUMERATED:
if (uctl->type != SND_CONTROL_TYPE_ENUMERATED) {
error("A wrong type %i for the control '%s'. The type integer is expected. Skipping...", uctl->type, control_id(&uctl->c.id));
return 1;
}
for (idx = 0; idx < cctl->info.values_count; idx++) {
if (cctl->info.value.enumerated.items <= uctl->c.value.enumerated.item[idx]) {
error("The value %u for the control '%s' is out of range 0-%i.", uctl->c.value.integer.value[idx], control_id(&uctl->c.id), cctl->info.value.enumerated.items-1);
return 1;
}
if (cctl->c.value.enumerated.item[idx] != uctl->c.value.enumerated.item[idx]) {
cctl->change = 1;
cctl->c.value.enumerated.item[idx] = uctl->c.value.enumerated.item[idx];
}
}
break;
case SND_CONTROL_TYPE_BYTES:
if (uctl->type != SND_CONTROL_TYPE_BYTES) {
error("A wrong type %i for the control %s. The type 'bytes' is expected. Skipping...", uctl->type, control_id(&uctl->c.id));
return 1;
}
if (memcmp(cctl->c.value.bytes.data, uctl->c.value.bytes.data, uctl->info.values_count)) {
cctl->change = 1;
memcpy(cctl->c.value.bytes.data, uctl->c.value.bytes.data, uctl->info.values_count);
}
break;
default:
error("The control type %i is not known.", cctl->type);
}
return 0;
}
static int soundcard_setup_merge_control(struct ctl_control *cctl, struct ctl_control *uctl, int cardno)
{
struct ctl_control *cctl1;
for ( ; uctl; uctl = uctl->next) {
for (cctl1 = cctl; cctl1; cctl1 = cctl1->next) {
if (cctl1->c.id.iface == uctl->c.id.iface &&
cctl1->c.id.device == uctl->c.id.device &&
cctl1->c.id.subdevice == uctl->c.id.subdevice &&
!strncmp(cctl1->c.id.name, uctl->c.id.name, sizeof(cctl1->c.id.name)) &&
cctl1->c.id.index == uctl->c.id.index) {
merge_one_control(cctl1, uctl, cardno);
break;
}
}
if (!cctl1) {
error("Cannot find the control %s...", control_id(&uctl->c.id));
}
}
return 0;
}
int soundcard_setup_merge_controls(int cardno)
{
struct soundcard *soundcard, *rsoundcard;
for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
break;
}
if (!soundcard) {
error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
continue;
}
if (cardno >= 0 && soundcard->no != cardno)
continue;
soundcard_setup_merge_control(soundcard->control.controls, rsoundcard->control.controls, soundcard->no);
}
return 0;
}
static int soundcard_open_ctl(snd_ctl_t **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;
}
int soundcard_setup_process_controls(int cardno)
{
int err;
snd_ctl_t *ctlhandle = NULL;
struct soundcard *soundcard;
struct ctl_control *ctl;
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (ctl = soundcard->control.controls; ctl; ctl = ctl->next) {
if (ctl->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_cwrite(ctlhandle, &ctl->c)) < 0)
error("Control '%s' write error: %s", control_id(&ctl->c.id), snd_strerror(err));
}
}
if (ctlhandle) {
snd_ctl_close(ctlhandle);
ctlhandle = NULL;
}
}
return 0;
}

View file

@ -1,530 +0,0 @@
/*
* 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;
struct soundcard *rsoundcards = NULL;
/*
* misc functions
*/
char *control_id(snd_control_id_t *id)
{
static char str[128];
if (!id)
return "???";
sprintf(str, "%i,%i,%i,%s,%i", id->iface, id->device, id->subdevice, id->name, id->index);
return str;
}
/*
* free functions
*/
static void soundcard_ctl_control_free(struct ctl_control *first)
{
struct ctl_control *next;
while (first) {
next = first->next;
free(first);
first = next;
}
}
static void soundcard_free1(struct soundcard *soundcard)
{
if (!soundcard)
return;
soundcard_ctl_control_free(soundcard->control.controls);
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);
soundcard_free(rsoundcards);
soundcards = NULL;
}
static int determine_controls(void *handle, struct ctl_control **cctl)
{
int err, idx;
snd_control_list_t list;
snd_control_id_t *item;
snd_control_t ctl;
struct ctl_control *prev_control;
struct ctl_control *new_control;
*cctl = NULL;
bzero(&list, sizeof(list));
if ((err = snd_ctl_clist(handle, &list)) < 0) {
error("Cannot determine controls: %s", snd_strerror(err));
return 1;
}
if (list.controls <= 0)
return 0;
list.controls_request = list.controls + 16;
list.controls_offset = list.controls_count = 0;
list.pids = malloc(sizeof(snd_control_id_t) * list.controls_request);
if (!list.pids) {
error("No enough memory...");
return 1;
}
if ((err = snd_ctl_clist(handle, &list)) < 0) {
error("Cannot determine controls (2): %s", snd_strerror(err));
return 1;
}
for (idx = 0, prev_control = NULL; idx < list.controls_count; idx++) {
item = &list.pids[idx];
bzero(&ctl, sizeof(ctl));
ctl.id = *item;
if ((err = snd_ctl_cread(handle, &ctl)) < 0) {
error("Cannot read control '%s': %s", control_id(item), snd_strerror(err));
free(list.pids);
return 1;
}
new_control = malloc(sizeof(*new_control));
if (!new_control) {
error("No enough memory...");
free(list.pids);
return 1;
}
bzero(new_control, sizeof(*new_control));
memcpy(&new_control->c, &ctl, sizeof(new_control->c));
new_control->info.id = ctl.id;
if ((err = snd_ctl_cinfo(handle, &new_control->info)) < 0) {
error("Cannot read control info '%s': %s", control_id(item), snd_strerror(err));
free(new_control);
free(list.pids);
return 1;
}
if (*cctl) {
prev_control->next = new_control;
prev_control = new_control;
} else {
*cctl = prev_control = new_control;
}
}
free(list.pids);
return 0;
}
static int soundcard_setup_collect_controls1(int cardno)
{
snd_ctl_t *handle;
struct soundcard *card, *first, *prev;
int err;
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; first = first->next) {
if (first->no > cardno) {
if (!prev) {
soundcards = card;
} else {
prev->next = card;
}
card->next = first;
break;
}
prev = first;
}
if (!first) {
if (!soundcards) {
soundcards = card;
} else {
prev->next = 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;
}
/* --- */
if (determine_controls(handle, &card->control.controls)) {
snd_ctl_close(handle);
return 1;
}
/* --- */
snd_ctl_close(handle);
return 0;
}
int soundcard_setup_collect_controls(int cardno)
{
int err;
unsigned int mask;
if (cardno >= 0) {
return soundcard_setup_collect_controls1(cardno);
} else {
mask = snd_cards_mask();
for (cardno = 0; cardno < SND_CARDS; cardno++) {
if (!(mask & (1 << cardno)))
continue;
err = soundcard_setup_collect_controls1(cardno);
if (err)
return err;
}
return 0;
}
}
int soundcard_setup_load(const char *cfgfile, int skip)
{
extern int yyparse(void);
extern int linecount;
extern FILE *yyin;
#ifdef YYDEBUG
extern int yydebug;
#endif
int xtry;
#ifdef YYDEBUG
yydebug = 1;
#endif
if (debugflag)
printf("cfgfile = '%s'\n", cfgfile);
if (skip && access(cfgfile, R_OK))
return 0;
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_control(FILE * out, const char *space, int card, struct ctl_control *control)
{
char *s, v[16];
int err, idx;
snd_ctl_t *handle;
snd_control_info_t info;
memcpy(&info, &control->info, sizeof(info));
v[0] = '\0';
switch (info.type) {
case SND_CONTROL_TYPE_BOOLEAN: s = "bool"; break;
case SND_CONTROL_TYPE_INTEGER: s = "integer"; break;
case SND_CONTROL_TYPE_ENUMERATED: s = "enumerated"; break;
case SND_CONTROL_TYPE_BYTES: s = "bytes"; break;
default: s = "unknown";
}
fprintf(out, "\n%s; The type is '%s'. Access:", space, s);
if (info.access & SND_CONTROL_ACCESS_READ)
fprintf(out, " read");
if (info.access & SND_CONTROL_ACCESS_WRITE)
fprintf(out, " write");
if (info.access & SND_CONTROL_ACCESS_INACTIVE)
fprintf(out, " inactive");
fprintf(out, ". Count is %i.\n", info.values_count);
switch (info.type) {
case SND_CONTROL_TYPE_BOOLEAN:
if (info.value.integer.min != 0 || info.value.integer.max != 1 ||
info.value.integer.step != 0)
error("Wrong control '%s' (boolean)\n", control_id(&info.id));
break;
case SND_CONTROL_TYPE_INTEGER:
fprintf(out, "%s; The range is %li-%li (step %li)\n", space, info.value.integer.min, info.value.integer.max, info.value.integer.step);
break;
case SND_CONTROL_TYPE_ENUMERATED:
if ((err = snd_ctl_open(&handle, card)) >= 0) {
for (idx = 0; idx < info.value.enumerated.items; idx++) {
info.value.enumerated.item = idx;
if (snd_ctl_cinfo(handle, &info) >= 0)
fprintf(out, "%s; Item #%i - %s\n", space, idx, info.value.enumerated.name);
}
snd_ctl_close(handle);
}
break;
default:
break;
}
switch (info.id.iface) {
case SND_CONTROL_IFACE_CARD: s = "global"; break;
case SND_CONTROL_IFACE_HWDEP: s = "hwdep"; break;
case SND_CONTROL_IFACE_MIXER: s = "mixer"; break;
case SND_CONTROL_IFACE_PCM: s = "pcm"; break;
case SND_CONTROL_IFACE_RAWMIDI: s = "rawmidi"; break;
case SND_CONTROL_IFACE_TIMER: s = "timer"; break;
case SND_CONTROL_IFACE_SEQUENCER: s = "sequencer"; break;
default: sprintf(v, "%i", info.id.iface); s = v; break;
}
fprintf(out, "%scontrol(ident={iface=%s", space, s);
if (info.id.device > 0)
fprintf(out, ", device=%i", info.id.device);
if (info.id.subdevice > 0)
fprintf(out, ", subdevice=%i", info.id.subdevice);
fprintf(out, ", name='%s'", info.id.name);
if (info.id.index > 0)
fprintf(out, ", index=%i", info.id.index);
fprintf(out, "}, ");
switch (info.type) {
case SND_CONTROL_TYPE_BOOLEAN: fprintf(out, "bool={"); break;
case SND_CONTROL_TYPE_INTEGER: fprintf(out, "int={"); break;
case SND_CONTROL_TYPE_ENUMERATED: fprintf(out, "enum={"); break;
case SND_CONTROL_TYPE_BYTES: fprintf(out, "byte={"); break;
default: break;
}
for (idx = 0; idx < info.values_count; idx++) {
if (idx > 0)
fprintf(out, ",");
switch (info.type) {
case SND_CONTROL_TYPE_BOOLEAN:
fprintf(out, "%s", control->c.value.integer.value[idx] ? "true" : "false");
break;
case SND_CONTROL_TYPE_INTEGER:
fprintf(out, "%li", control->c.value.integer.value[idx]);
break;
case SND_CONTROL_TYPE_ENUMERATED:
fprintf(out, "%u", control->c.value.enumerated.item[idx]);
break;
case SND_CONTROL_TYPE_BYTES:
fprintf(out, "%02x", control->c.value.bytes.data[idx]);
break;
default:
break;
}
}
fprintf(out, "})\n");
}
static void soundcard_setup_write_controls(FILE *out, const char *space, int card, struct ctl_control **controls)
{
struct ctl_control *ctl;
if (!(*controls))
return;
for (ctl = *controls; ctl; ctl = ctl->next)
soundcard_setup_write_control(out, space, card, ctl);
}
#define MAX_LINE (32 * 1024)
int soundcard_setup_write(const char *cfgfile, int cardno)
{
FILE *out, *out1, *out2, *in;
char *tmpfile1, *tmpfile2;
struct soundcard *first, *sel = NULL;
char *line, cardname[sizeof(first->control.hwinfo.name)+16], *ptr1;
int mark, size, ok;
tmpfile1 = (char *)malloc(strlen(cfgfile) + 16);
tmpfile2 = (char *)malloc(strlen(cfgfile) + 16);
if (!tmpfile1 || !tmpfile2) {
error("No enough memory...\n");
if (tmpfile1)
free(tmpfile1);
if (tmpfile2)
free(tmpfile2);
return 1;
}
strcpy(tmpfile1, cfgfile);
strcat(tmpfile1, ".new");
strcpy(tmpfile2, cfgfile);
strcat(tmpfile2, ".insert");
if (cardno >= 0) {
line = (char *)malloc(MAX_LINE);
if (!line) {
error("No enough memory...\n");
return 1;
}
if ((in = fopen(cfgfile, "r")) == NULL)
cardno = -1;
} else {
line = NULL;
in = NULL;
}
if ((out = out1 = fopen(tmpfile1, "w+")) == NULL) {
error("Cannot open file '%s' for writing...\n", tmpfile1);
return 1;
}
fprintf(out, "# ALSA driver configuration\n");
fprintf(out, "# This configuration is generated with the alsactl program.\n");
fprintf(out, "\n");
if (cardno >= 0) {
if ((out = out2 = fopen(tmpfile2, "w+")) == NULL) {
error("Cannot open file '%s' for writing...\n", tmpfile2);
return 1;
}
} else {
out2 = NULL;
}
for (first = soundcards; first; first = first->next) {
if (cardno >= 0 && first->no != cardno)
continue;
sel = first;
fprintf(out, "soundcard(\"%s\") {\n", first->control.hwinfo.id);
if (first->control.controls) {
soundcard_setup_write_controls(out, " ", first->no, &first->control.controls);
}
fprintf(out, "}\n%s", cardno < 0 && first->next ? "\n" : "");
}
/* merge the old and new text */
if (cardno >= 0) {
fseek(out2, 0, SEEK_SET);
mark = ok = 0;
__1:
while (fgets(line, MAX_LINE - 1, in)) {
line[MAX_LINE - 1] = '\0';
if (!strncmp(line, "soundcard(", 10))
break;
}
while (!feof(in)) {
ptr1 = line + 10;
while (*ptr1 && *ptr1 != '"')
ptr1++;
if (*ptr1)
ptr1++;
strncpy(cardname, sel->control.hwinfo.id, sizeof(sel->control.hwinfo.id));
cardname[sizeof(sel->control.hwinfo.id)] = '\0';
strcat(cardname, "\"");
if (!strncmp(ptr1, cardname, strlen(cardname))) {
if (mark)
fprintf(out1, "\n");
do {
size = fread(line, 1, MAX_LINE, out2);
if (size > 0)
fwrite(line, 1, size, out1);
} while (size > 0);
mark = ok = 1;
goto __1;
} else {
if (mark)
fprintf(out1, "\n");
fprintf(out1, line);
while (fgets(line, MAX_LINE - 1, in)) {
line[MAX_LINE - 1] = '\0';
fprintf(out1, line);
if (line[0] == '}') {
mark = 1;
goto __1;
}
}
}
}
if (!ok) {
if (mark)
fprintf(out1, "\n");
do {
size = fread(line, 1, MAX_LINE, out2);
printf("size = %i\n", size);
if (size > 0)
fwrite(line, 1, size, out1);
} while (size > 0);
}
}
if (in)
fclose(in);
if (out2)
fclose(out2);
if (!access(cfgfile, F_OK) && remove(cfgfile))
error("Cannot remove file '%s'...", cfgfile);
if (rename(tmpfile1, cfgfile) < 0)
error("Cannot rename file '%s' to '%s'...", tmpfile1, cfgfile);
fclose(out1);
if (line)
free(line);
if (tmpfile2) {
remove(tmpfile2);
free(tmpfile2);
}
if (tmpfile1) {
remove(tmpfile1);
free(tmpfile1);
}
return 0;
}