mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-14 17:25:41 +01:00
1025 lines
30 KiB
C
1025 lines
30 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
static char *mixer_element_name(snd_mixer_eid_t *eid)
|
|
{
|
|
static char str[32];
|
|
|
|
if (!eid)
|
|
return "???";
|
|
strncpy(str, eid->name, sizeof(eid->name));
|
|
str[sizeof(eid->name)] = '\0';
|
|
return str;
|
|
}
|
|
|
|
char *mixer_element_id(snd_mixer_eid_t *eid)
|
|
{
|
|
static char str[64];
|
|
|
|
if (!eid)
|
|
return "???";
|
|
sprintf(str, "%s:%i:%i", mixer_element_name(eid), eid->index, eid->type);
|
|
return str;
|
|
}
|
|
|
|
/*
|
|
* 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_element_free(struct mixer_element *first)
|
|
{
|
|
struct mixer_element *next;
|
|
|
|
while (first) {
|
|
next = first->next;
|
|
snd_mixer_element_info_free(&first->info);
|
|
snd_mixer_element_free(&first->element);
|
|
free(first);
|
|
first = next;
|
|
}
|
|
}
|
|
|
|
static void soundcard_mixer_free1(struct mixer *mixer)
|
|
{
|
|
if (!mixer)
|
|
return;
|
|
soundcard_mixer_element_free(mixer->elements);
|
|
soundcard_ctl_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_free1(struct pcm *pcm)
|
|
{
|
|
if (!pcm)
|
|
return;
|
|
soundcard_ctl_switch_free(pcm->pswitches);
|
|
soundcard_ctl_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_free1(struct rawmidi *rawmidi)
|
|
{
|
|
if (!rawmidi)
|
|
return;
|
|
soundcard_ctl_switch_free(rawmidi->iswitches);
|
|
soundcard_ctl_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);
|
|
soundcard_free(rsoundcards);
|
|
soundcards = NULL;
|
|
}
|
|
|
|
static int determine_switches(void *handle, struct ctl_switch **csw, int iface, int device, int stream)
|
|
{
|
|
int err, idx;
|
|
snd_switch_list_t list;
|
|
snd_switch_list_item_t *item;
|
|
snd_switch_t sw;
|
|
struct ctl_switch *prev_csw;
|
|
struct ctl_switch *new_csw;
|
|
|
|
*csw = NULL;
|
|
bzero(&list, sizeof(list));
|
|
list.iface = iface;
|
|
list.device = device;
|
|
list.stream = stream;
|
|
if ((err = snd_ctl_switch_list(handle, &list)) < 0) {
|
|
error("Cannot determine switches for interface %i and device %i and stream %i: %s", iface, device, stream, snd_strerror(err));
|
|
return 1;
|
|
}
|
|
if (list.switches_over <= 0)
|
|
return 0;
|
|
list.switches_size = list.switches_over + 16;
|
|
list.switches = list.switches_over = 0;
|
|
list.pswitches = malloc(sizeof(snd_switch_list_item_t) * list.switches_size);
|
|
if (!list.pswitches) {
|
|
error("No enough memory...");
|
|
return 1;
|
|
}
|
|
if ((err = snd_ctl_switch_list(handle, &list)) < 0) {
|
|
error("Cannot determine switches (2) for interface %i and device %i and stream %i: %s", iface, device, stream, snd_strerror(err));
|
|
return 1;
|
|
}
|
|
for (idx = 0, prev_csw = NULL; idx < list.switches; idx++) {
|
|
item = &list.pswitches[idx];
|
|
bzero(&sw, sizeof(sw));
|
|
sw.iface = iface;
|
|
sw.device = device;
|
|
sw.stream = stream;
|
|
strncpy(sw.name, item->name, sizeof(sw.name));
|
|
if ((err = snd_ctl_switch_read(handle, &sw)) < 0) {
|
|
error("Cannot read switch '%s' for interface %i and device %i and stream %i: %s", sw.name, iface, device, stream, snd_strerror(err));
|
|
free(list.pswitches);
|
|
return 1;
|
|
}
|
|
new_csw = malloc(sizeof(*new_csw));
|
|
if (!new_csw) {
|
|
error("No enough memory...");
|
|
free(list.pswitches);
|
|
return 1;
|
|
}
|
|
bzero(new_csw, sizeof(*new_csw));
|
|
memcpy(&new_csw->s, &sw, sizeof(new_csw->s));
|
|
if (*csw) {
|
|
prev_csw->next = new_csw;
|
|
prev_csw = new_csw;
|
|
} else {
|
|
*csw = prev_csw = new_csw;
|
|
}
|
|
}
|
|
free(list.pswitches);
|
|
return 0;
|
|
}
|
|
|
|
static int soundcard_setup_collect_switches1(int cardno)
|
|
{
|
|
snd_ctl_t *handle;
|
|
snd_mixer_t *mhandle;
|
|
struct soundcard *card, *first, *prev;
|
|
int err, device;
|
|
struct mixer *mixer, *mixerprev;
|
|
struct pcm *pcm, *pcmprev;
|
|
struct rawmidi *rawmidi, *rawmidiprev;
|
|
|
|
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_switches(handle, &card->control.switches, SND_CTL_IFACE_CONTROL, 0, 0)) {
|
|
snd_ctl_close(handle);
|
|
return 1;
|
|
}
|
|
/* --- */
|
|
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;
|
|
if (determine_switches(handle, &mixer->switches, SND_CTL_IFACE_MIXER, device, 0)) {
|
|
snd_ctl_close(handle);
|
|
return 1;
|
|
}
|
|
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", 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", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
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;
|
|
pcm->info.device = device;
|
|
if ((err = snd_ctl_pcm_info(handle, &pcm->info)) < 0) {
|
|
snd_ctl_close(handle);
|
|
error("PCM info error: %s", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
if (determine_switches(handle, &pcm->pswitches, SND_CTL_IFACE_PCM, device, SND_PCM_STREAM_PLAYBACK)) {
|
|
snd_ctl_close(handle);
|
|
return 1;
|
|
}
|
|
if (determine_switches(handle, &pcm->rswitches, SND_CTL_IFACE_PCM, device, SND_PCM_STREAM_CAPTURE)) {
|
|
snd_ctl_close(handle);
|
|
return 1;
|
|
}
|
|
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;
|
|
rawmidi->info.device = device;
|
|
if ((err = snd_ctl_rawmidi_info(handle, &rawmidi->info)) < 0) {
|
|
snd_ctl_close(handle);
|
|
error("RAWMIDI info error: %s", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
if (determine_switches(handle, &rawmidi->oswitches, SND_CTL_IFACE_RAWMIDI, device, SND_RAWMIDI_STREAM_OUTPUT)) {
|
|
snd_ctl_close(handle);
|
|
return 1;
|
|
}
|
|
if (determine_switches(handle, &rawmidi->iswitches, SND_CTL_IFACE_RAWMIDI, device, SND_RAWMIDI_STREAM_INPUT)) {
|
|
snd_ctl_close(handle);
|
|
return 1;
|
|
}
|
|
if (!rawmidiprev) {
|
|
card->rawmidis = rawmidi;
|
|
} else {
|
|
rawmidiprev->next = rawmidi;
|
|
}
|
|
rawmidiprev = rawmidi;
|
|
}
|
|
/* --- */
|
|
snd_ctl_close(handle);
|
|
return 0;
|
|
}
|
|
|
|
int soundcard_setup_collect_switches(int cardno)
|
|
{
|
|
int err;
|
|
unsigned int mask;
|
|
|
|
if (cardno >= 0) {
|
|
return soundcard_setup_collect_switches1(cardno);
|
|
} else {
|
|
mask = snd_cards_mask();
|
|
for (cardno = 0; cardno < SND_CARDS; cardno++) {
|
|
if (!(mask & (1 << cardno)))
|
|
continue;
|
|
err = soundcard_setup_collect_switches1(cardno);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int soundcard_setup_collect_data1(int cardno)
|
|
{
|
|
snd_ctl_t *handle;
|
|
snd_mixer_t *mhandle;
|
|
struct soundcard *card;
|
|
int err, idx;
|
|
struct mixer *mixer;
|
|
snd_mixer_elements_t elements;
|
|
struct mixer_element *mixerelement, *mixerelementprev;
|
|
|
|
if ((err = snd_ctl_open(&handle, cardno)) < 0) {
|
|
error("SND CTL open error: %s", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
/* --- */
|
|
for (card = soundcards; card && card->no != cardno; card = card->next);
|
|
if (!card) {
|
|
snd_ctl_close(handle);
|
|
error("The soundcard %i does not exist.", cardno);
|
|
return 1;
|
|
}
|
|
for (mixer = card->mixers; mixer; mixer = mixer->next) {
|
|
if ((err = snd_mixer_open(&mhandle, cardno, mixer->no)) < 0) {
|
|
snd_ctl_close(handle);
|
|
error("MIXER open error: %s", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
if (mixer->elements)
|
|
soundcard_mixer_element_free(mixer->elements);
|
|
mixer->elements = NULL;
|
|
bzero(&elements, sizeof(elements));
|
|
if ((err = snd_mixer_elements(mhandle, &elements)) < 0) {
|
|
snd_mixer_close(mhandle);
|
|
snd_ctl_close(handle);
|
|
error("MIXER elements error: %s", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
elements.elements_size = elements.elements_over + 16;
|
|
elements.elements = elements.elements_over = 0;
|
|
elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_size * sizeof(snd_mixer_eid_t));
|
|
if ((err = snd_mixer_elements(mhandle, &elements)) < 0) {
|
|
snd_mixer_close(mhandle);
|
|
snd_ctl_close(handle);
|
|
error("MIXER elements (2) error: %s", snd_strerror(err));
|
|
return 1;
|
|
}
|
|
for (idx = 0, mixerelementprev = NULL; idx < elements.elements; idx++) {
|
|
mixerelement = (struct mixer_element *) malloc(sizeof(struct mixer_element));
|
|
if (!mixerelement) {
|
|
snd_mixer_close(mhandle);
|
|
snd_ctl_close(handle);
|
|
error("malloc problem");
|
|
return 1;
|
|
}
|
|
bzero(mixerelement, sizeof(*mixerelement));
|
|
mixerelement->info.eid = elements.pelements[idx];
|
|
mixerelement->element.eid = elements.pelements[idx];
|
|
if (snd_mixer_element_has_info(&elements.pelements[idx]) == 1) {
|
|
if ((err = snd_mixer_element_info_build(mhandle, &mixerelement->info)) < 0) {
|
|
free(mixerelement);
|
|
error("MIXER element %s info error (%s) - skipping", mixer_element_id(&mixerelement->info.eid), snd_strerror(err));
|
|
break;
|
|
}
|
|
}
|
|
if (snd_mixer_element_has_control(&elements.pelements[idx]) == 1) {
|
|
if ((err = snd_mixer_element_build(mhandle, &mixerelement->element)) < 0) {
|
|
free(mixerelement);
|
|
error("MIXER element %s build error (%s) - skipping", mixer_element_id(&mixerelement->element.eid), snd_strerror(err));
|
|
break;
|
|
}
|
|
}
|
|
if (!mixerelementprev) {
|
|
mixer->elements = mixerelement;
|
|
} else {
|
|
mixerelementprev->next = mixerelement;
|
|
}
|
|
mixerelementprev = mixerelement;
|
|
}
|
|
free(elements.pelements);
|
|
snd_mixer_close(mhandle);
|
|
}
|
|
/* --- */
|
|
snd_ctl_close(handle);
|
|
return 0;
|
|
}
|
|
|
|
int soundcard_setup_collect_data(int cardno)
|
|
{
|
|
int err;
|
|
unsigned int mask;
|
|
|
|
if (cardno >= 0) {
|
|
return soundcard_setup_collect_data1(cardno);
|
|
} else {
|
|
mask = snd_cards_mask();
|
|
for (cardno = 0; cardno < SND_CARDS; cardno++) {
|
|
if (!(mask & (1 << cardno)))
|
|
continue;
|
|
err = soundcard_setup_collect_data1(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;
|
|
extern int yydebug;
|
|
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_switch(FILE * out, const char *space, int card, int interface, snd_switch_t *sw)
|
|
{
|
|
char *s, v[16];
|
|
int idx, first, switchok = 0;
|
|
|
|
v[0] = '\0';
|
|
switch (sw->type) {
|
|
case SND_SW_TYPE_BOOLEAN:
|
|
s = "bool";
|
|
strcpy(v, sw->value.enable ? "true" : "false");
|
|
break;
|
|
case SND_SW_TYPE_BYTE:
|
|
s = "byte";
|
|
if (sw->subtype == SND_SW_SUBTYPE_HEXA) {
|
|
sprintf(v, "0x%x", (unsigned int) sw->value.data8[0]);
|
|
} else {
|
|
sprintf(v, "%u", (unsigned int) sw->value.data8[0]);
|
|
}
|
|
break;
|
|
case SND_SW_TYPE_WORD:
|
|
s = "word";
|
|
if (sw->subtype == SND_SW_SUBTYPE_HEXA) {
|
|
sprintf(v, "0x%x", (unsigned int) sw->value.data16[0]);
|
|
} else {
|
|
sprintf(v, "%u", (unsigned int) sw->value.data16[0]);
|
|
}
|
|
break;
|
|
case SND_SW_TYPE_DWORD:
|
|
s = "dword";
|
|
if (sw->subtype == SND_SW_SUBTYPE_HEXA) {
|
|
sprintf(v, "0x%x", sw->value.data32[0]);
|
|
} else {
|
|
sprintf(v, "%u", sw->value.data32[0]);
|
|
}
|
|
break;
|
|
case SND_SW_TYPE_USER:
|
|
s = "user";
|
|
break;
|
|
case SND_SW_TYPE_LIST:
|
|
s = "list";
|
|
sprintf(v, "%u", sw->value.item_number);
|
|
break;
|
|
case SND_SW_TYPE_USER_READ_ONLY:
|
|
return;
|
|
default:
|
|
s = "unknown";
|
|
}
|
|
fprintf(out, "%s; The type is '%s'.\n", space, s);
|
|
if (sw->low != 0 || sw->high != 0)
|
|
fprintf(out, "%s; The accepted switch range is from %u to %u.\n", space, sw->low, sw->high);
|
|
if (sw->type == SND_SW_TYPE_LIST) {
|
|
snd_ctl_t *handle;
|
|
snd_switch_t swi;
|
|
memset(&swi, 0, sizeof(swi));
|
|
swi.iface = sw->iface;
|
|
swi.device = sw->device;
|
|
swi.stream = sw->stream;
|
|
strncpy(swi.name, sw->name, sizeof(swi.name));
|
|
swi.type = SND_SW_TYPE_LIST_ITEM;
|
|
if (snd_ctl_open(&handle, card) >= 0) {
|
|
for (idx = sw->low; idx <= sw->high; idx++) {
|
|
swi.low = swi.high = idx;
|
|
if (snd_ctl_switch_read(handle, &swi) >= 0)
|
|
fprintf(out, "%s; Item #%i - %s\n", space, swi.low, swi.value.item);
|
|
}
|
|
snd_ctl_close(handle);
|
|
}
|
|
}
|
|
if (interface == SND_INTERFACE_CONTROL && sw->type == SND_SW_TYPE_WORD &&
|
|
!strcmp(sw->name, SND_CTL_SW_JOYSTICK_ADDRESS)) {
|
|
for (idx = 1, first = 1; idx < 16; idx++) {
|
|
if (sw->value.data16[idx]) {
|
|
if (first) {
|
|
fprintf(out, "%s; Available addresses - 0x%x", space, sw->value.data16[idx]);
|
|
first = 0;
|
|
} else {
|
|
fprintf(out, ", 0x%x", sw->value.data16[idx]);
|
|
}
|
|
}
|
|
}
|
|
if (!first)
|
|
fprintf(out, "\n");
|
|
}
|
|
if (interface == SND_INTERFACE_MIXER && sw->type == SND_SW_TYPE_BOOLEAN &&
|
|
!strcmp(sw->name, SND_MIXER_SW_IEC958_OUTPUT)) {
|
|
fprintf(out, "%sswitch(\"%s\",", space, sw->name);
|
|
if (sw->value.data32[1] == (('C' << 8) | 'S')) { /* Cirrus Crystal */
|
|
switchok = 0;
|
|
fprintf(out, "iec958ocs(%s", sw->value.enable ? "enable" : "disable");
|
|
if (sw->value.data16[4] & 0x2000)
|
|
fprintf(out, ",3d");
|
|
if (sw->value.data16[4] & 0x0040)
|
|
fprintf(out, ",reset");
|
|
if (sw->value.data16[4] & 0x0020)
|
|
fprintf(out, ",user");
|
|
if (sw->value.data16[4] & 0x0010)
|
|
fprintf(out, ",valid");
|
|
if (sw->value.data16[5] & 0x0002)
|
|
fprintf(out, ",data");
|
|
if (!(sw->value.data16[5] & 0x0004))
|
|
fprintf(out, ",protect");
|
|
switch (sw->value.data16[5] & 0x0018) {
|
|
case 0x0008:
|
|
fprintf(out, ",pre2");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (sw->value.data16[5] & 0x0020)
|
|
fprintf(out, ",fsunlock");
|
|
fprintf(out, ",type(0x%x)", (sw->value.data16[5] >> 6) & 0x7f);
|
|
if (sw->value.data16[5] & 0x2000)
|
|
fprintf(out, ",gstatus");
|
|
fprintf(out, ")");
|
|
goto __end;
|
|
}
|
|
}
|
|
fprintf(out, "%sswitch(\"%s\", ", space, sw->name);
|
|
if (!switchok) {
|
|
fprintf(out, v);
|
|
if (sw->type < 0 || sw->type > SND_SW_TYPE_LAST) {
|
|
/* TODO: some well known types should be verbose */
|
|
fprintf(out, "rawdata(@");
|
|
for (idx = 0; idx < 31; idx++) {
|
|
fprintf(out, "%02x:", sw->value.data8[idx]);
|
|
}
|
|
fprintf(out, "%02x@)", sw->value.data8[31]);
|
|
}
|
|
}
|
|
__end:
|
|
fprintf(out, ")\n");
|
|
}
|
|
|
|
static void soundcard_setup_write_switches(FILE *out, const char *space, int card, int interface, struct ctl_switch **switches)
|
|
{
|
|
struct ctl_switch *sw;
|
|
|
|
if (!(*switches))
|
|
return;
|
|
for (sw = *switches; sw; sw = sw->next)
|
|
soundcard_setup_write_switch(out, space, card, interface, &sw->s);
|
|
}
|
|
|
|
static void soundcard_setup_write_mixer_element(FILE * out, struct mixer_element * xelement)
|
|
{
|
|
snd_mixer_element_info_t *info;
|
|
snd_mixer_element_t *element;
|
|
int idx;
|
|
|
|
info = &xelement->info;
|
|
element = &xelement->element;
|
|
if (snd_mixer_element_has_control(&element->eid) != 1)
|
|
return;
|
|
switch (element->eid.type) {
|
|
case SND_MIXER_ETYPE_SWITCH1:
|
|
fprintf(out, " element(\"%s\",%i,%i,Switch1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
for (idx = 0; idx < element->data.switch1.sw; idx++)
|
|
fprintf(out, "%s%s", idx > 0 ? "," : "", snd_mixer_get_bit(element->data.switch1.psw, idx) ? "on" : "off");
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_SWITCH2:
|
|
fprintf(out, " element(\"%s\",%i,%i,Switch2(%s))\n", mixer_element_name(&element->eid), element->eid.index, element->eid.type, element->data.switch2.sw ? "on" : "off");
|
|
break;
|
|
case SND_MIXER_ETYPE_SWITCH3:
|
|
fprintf(out, " element(\"%s\",%i,%i,Switch3(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
for (idx = 0; idx < element->data.switch3.rsw; idx++)
|
|
fprintf(out, "%s%s", idx > 0 ? "," : "", snd_mixer_get_bit(element->data.switch3.prsw, idx) ? "on" : "off");
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_VOLUME1:
|
|
for (idx = 0; idx < info->data.volume1.range; idx++)
|
|
fprintf(out, " ; Channel %i : Min %i Max %i\n", idx, info->data.volume1.prange[idx].min, info->data.volume1.prange[idx].max);
|
|
fprintf(out, " element(\"%s\",%i,%i,Volume1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
for (idx = 0; idx < element->data.volume1.channels; idx++)
|
|
fprintf(out, "%s%i", idx > 0 ? "," : "", element->data.volume1.pchannels[idx]);
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_ACCU3:
|
|
for (idx = 0; idx < info->data.accu3.range; idx++)
|
|
fprintf(out, " ; Channel %i : Min %i Max %i\n", idx, info->data.accu3.prange[idx].min, info->data.accu3.prange[idx].max);
|
|
fprintf(out, " element(\"%s\",%i,%i,Accu3(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
for (idx = 0; idx < element->data.accu3.channels; idx++)
|
|
fprintf(out, "%s%i", idx > 0 ? "," : "", element->data.accu3.pchannels[idx]);
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_MUX1:
|
|
fprintf(out, " element(\"%s\",%i,%i,Mux1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
for (idx = 0; idx < element->data.mux1.sel; idx++) {
|
|
fprintf(out, "%selement(\"%s\",%i,%i)", idx > 0 ? "," : "", mixer_element_name(&element->data.mux1.psel[idx]), element->data.mux1.psel[idx].index, element->data.mux1.psel[idx].type);
|
|
}
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_MUX2:
|
|
fprintf(out, " element(\"%s\",%i,%i,Mux2(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
fprintf(out, "element(\"%s\",%i,%i)", mixer_element_name(&element->data.mux2.sel), element->data.mux2.sel.index, element->data.mux2.sel.type);
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_TONE_CONTROL1:
|
|
if (info->data.tc1.tc & SND_MIXER_TC1_SW)
|
|
fprintf(out, " ; The tone control has an on/off switch.\n");
|
|
if (info->data.tc1.tc & SND_MIXER_TC1_BASS)
|
|
fprintf(out, " ; Bass : Min %i Max %i\n", info->data.tc1.min_bass, info->data.tc1.max_bass);
|
|
if (info->data.tc1.tc & SND_MIXER_TC1_TREBLE)
|
|
fprintf(out, " ; Treble : Min %i Max %i\n", info->data.tc1.min_treble, info->data.tc1.max_treble);
|
|
fprintf(out, " element(\"%s\",%i,%i,ToneControl1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
idx = 0;
|
|
if (element->data.tc1.tc & SND_MIXER_TC1_SW)
|
|
fprintf(out, "%ssw=%s", idx++ > 0 ? "," : "", element->data.tc1.sw ? "on" : "off");
|
|
if (element->data.tc1.tc & SND_MIXER_TC1_BASS)
|
|
fprintf(out, "%sbass=%i", idx++ > 0 ? "," : "", element->data.tc1.bass);
|
|
if (element->data.tc1.tc & SND_MIXER_TC1_TREBLE)
|
|
fprintf(out, "%streble=%i", idx++ > 0 ? "," : "", element->data.tc1.treble);
|
|
fprintf(out, "))\n");
|
|
break;
|
|
case SND_MIXER_ETYPE_3D_EFFECT1:
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_SW)
|
|
fprintf(out, " ; The 3D effect has an on/off switch.\n");
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_MONO_SW)
|
|
fprintf(out, " ; The 3D effect has an mono processing on/off switch.\n");
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_WIDE)
|
|
fprintf(out, " ; Wide : Min %i Max %i\n", info->data.teffect1.min_wide, info->data.teffect1.max_wide);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_VOLUME)
|
|
fprintf(out, " ; Volume : Min %i Max %i\n", info->data.teffect1.min_volume, info->data.teffect1.max_volume);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_CENTER)
|
|
fprintf(out, " ; Center : Min %i Max %i\n", info->data.teffect1.min_center, info->data.teffect1.max_center);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_SPACE)
|
|
fprintf(out, " ; Space : Min %i Max %i\n", info->data.teffect1.min_space, info->data.teffect1.max_space);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_DEPTH)
|
|
fprintf(out, " ; Depth : Min %i Max %i\n", info->data.teffect1.min_depth, info->data.teffect1.max_depth);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_DELAY)
|
|
fprintf(out, " ; Delay : Min %i Max %i\n", info->data.teffect1.min_delay, info->data.teffect1.max_delay);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_FEEDBACK)
|
|
fprintf(out, " ; Feedback : Min %i Max %i\n", info->data.teffect1.min_feedback, info->data.teffect1.max_feedback);
|
|
if (info->data.teffect1.effect & SND_MIXER_EFF1_DEPTH_REAR)
|
|
fprintf(out, " ; Depth rear : Min %i Max %i\n", info->data.teffect1.min_depth_rear, info->data.teffect1.max_depth_rear);
|
|
fprintf(out, " element(\"%s\",%i,%i,_3D_Effect1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
|
|
idx = 0;
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_SW)
|
|
fprintf(out, "%ssw=%s", idx++ > 0 ? "," : "", element->data.teffect1.sw ? "on" : "off");
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_MONO_SW)
|
|
fprintf(out, "%smono_sw=%s", idx++ > 0 ? "," : "", element->data.teffect1.mono_sw ? "on" : "off");
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_WIDE)
|
|
fprintf(out, "%swide=%i", idx++ > 0 ? "," : "", element->data.teffect1.wide);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_VOLUME)
|
|
fprintf(out, "%svolume=%i", idx++ > 0 ? "," : "", element->data.teffect1.volume);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_CENTER)
|
|
fprintf(out, "%scenter=%i", idx++ > 0 ? "," : "", element->data.teffect1.center);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_SPACE)
|
|
fprintf(out, "%sspace=%i", idx++ > 0 ? "," : "", element->data.teffect1.space);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_DEPTH)
|
|
fprintf(out, "%sdepth=%i", idx++ > 0 ? "," : "", element->data.teffect1.depth);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_DELAY)
|
|
fprintf(out, "%sdelay=%i", idx++ > 0 ? "," : "", element->data.teffect1.delay);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_FEEDBACK)
|
|
fprintf(out, "%sfeedback=%i", idx++ > 0 ? "," : "", element->data.teffect1.feedback);
|
|
if (element->data.teffect1.effect & SND_MIXER_EFF1_DEPTH_REAR)
|
|
fprintf(out, "%sdepth_rear=%i", idx++ > 0 ? "," : "", element->data.teffect1.depth_rear);
|
|
fprintf(out, "))\n");
|
|
break;
|
|
default:
|
|
fprintf(out, " ; Unknown element %s\n", mixer_element_id(&element->eid));
|
|
}
|
|
}
|
|
|
|
#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;
|
|
struct mixer *mixer;
|
|
struct mixer_element *mixerelement;
|
|
struct pcm *pcm;
|
|
struct rawmidi *rawmidi;
|
|
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.switches) {
|
|
fprintf(out, " control {\n");
|
|
soundcard_setup_write_switches(out, " ", first->no, SND_INTERFACE_CONTROL, &first->control.switches);
|
|
fprintf(out, " }\n");
|
|
}
|
|
for (mixer = first->mixers; mixer; mixer = mixer->next) {
|
|
fprintf(out, " mixer(\"%s\") {\n", mixer->info.name);
|
|
soundcard_setup_write_switches(out, " ", first->no, SND_INTERFACE_MIXER, &mixer->switches);
|
|
for (mixerelement = mixer->elements; mixerelement; mixerelement = mixerelement->next)
|
|
soundcard_setup_write_mixer_element(out, mixerelement);
|
|
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 {\n");
|
|
soundcard_setup_write_switches(out, " ", first->no, SND_INTERFACE_PCM, &pcm->pswitches);
|
|
fprintf(out, " }\n");
|
|
}
|
|
if (pcm->rswitches) {
|
|
fprintf(out, " capture {\n");
|
|
soundcard_setup_write_switches(out, " ", first->no, SND_INTERFACE_PCM, &pcm->rswitches);
|
|
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 {\n");
|
|
soundcard_setup_write_switches(out, " ", first->no, SND_INTERFACE_RAWMIDI, &rawmidi->oswitches);
|
|
fprintf(out, " }\n");
|
|
}
|
|
if (rawmidi->iswitches) {
|
|
fprintf(out, " input {\n");
|
|
soundcard_setup_write_switches(out, " ", first->no, SND_INTERFACE_RAWMIDI, &rawmidi->iswitches);
|
|
fprintf(out, " }\n");
|
|
}
|
|
fprintf(out, " }\n");
|
|
}
|
|
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;
|
|
}
|