1
0
Fork 0

Merge tag 'v1.2.15.1' of https://github.com/alsa-project/alsa-utils into android-16.0

Release v1.2.15.1

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Konsta 2025-12-31 15:16:08 +02:00
commit c1f0920da1
29 changed files with 2087 additions and 95 deletions

View file

@ -11,7 +11,7 @@ jobs:
- name: Prepare environment
run: |
dnf -y upgrade
dnf -y install @development-tools libtool bzip2 gettext-devel ncurses-devel
dnf -y install @development-tools libtool bzip2 gettext-devel ncurses-devel awk
- name: Checkout alsa-lib
uses: actions/checkout@v4

97
.github/workflows/coverity.yml vendored Normal file
View file

@ -0,0 +1,97 @@
name: Coverity Scan
on:
workflow_dispatch:
# push:
# branches:
# - master
schedule:
# Run once a week on Sunday at 4am UTC
- cron: '0 4 * * 0'
jobs:
coverity:
runs-on: ubuntu-latest
container:
image: fedora:latest
steps:
- name: Prepare environment
run: |
dnf -y upgrade
dnf -y install @development-tools libtool bzip2 gettext-devel ncurses-devel awk curl wget tar file
- name: Checkout alsa-lib
uses: actions/checkout@v4
with:
repository: alsa-project/alsa-lib
ref: master
path: alsa-lib
- name: Configure alsa-lib
run: |
cd alsa-lib
head -5 configure.ac
libtoolize --force --copy --automake
aclocal
autoheader
automake --foreign --copy --add-missing
autoconf
export CFLAGS="-O2 -Wall -W -Wunused-const-variable=0 -pipe -g"
./configure
echo "Version: $(cat version)"
- name: Build alsa-lib
run: |
cd alsa-lib
make
- name: Install alsa-lib
run: |
cd alsa-lib
make install
- name: Checkout alsa-utils
uses: actions/checkout@v4
with:
path: alsa-utils
- name: Configure alsa-utils
run: |
cd alsa-utils
mv configure.ac configure.ac.old
sed -e 's;po/Makefile.in;;' < configure.ac.old > configure.ac
aclocal
gettextize -c -f --no-changelog
libtoolize --force --copy --automake
aclocal -I m4
autoheader
automake --foreign --copy --add-missing
autoconf
export CFLAGS="-O2 -Wall -W -Wunused-const-variable=0 -pipe -g"
./configure
- name: Download Coverity Build Tool
run: |
cd alsa-utils
wget -q https://scan.coverity.com/download/linux64 \
--post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=${{ secrets.COVERITY_SCAN_PROJECT }}" \
-O coverity_tool.tar.gz
mkdir coverity-tool
tar xzf coverity_tool.tar.gz --strip-components=1 -C coverity-tool
- name: Build with Coverity Build Tool
run: |
cd alsa-utils
export PATH="$PWD/coverity-tool/bin:$PATH"
cov-build --dir cov-int make
- name: Submit results to Coverity Scan
run: |
cd alsa-utils
tar czvf alsa-utils.tgz cov-int
curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \
--form email=${{ secrets.COVERITY_SCAN_EMAIL }} \
--form file=@alsa-utils.tgz \
--form version="$(git describe --tags --always)" \
--form description="ALSA Utils Coverity Scan" \
https://scan.coverity.com/builds?project=alsa-project%2Falsa-utils

View file

@ -24,8 +24,10 @@ cc_binary {
name: "alsactl",
srcs: [
"alsactl/alsactl.c",
"alsactl/boot_params.c",
"alsactl/clean.c",
"alsactl/daemon.c",
"alsactl/export.c",
"alsactl/info.c",
"alsactl/init_parse.c",
"alsactl/init_ucm.c",
@ -33,6 +35,7 @@ cc_binary {
"alsactl/monitor.c",
"alsactl/state.c",
"alsactl/utils.c",
"alsactl/wait.c",
],
cflags: [
"-DSYS_ASOUNDRC=\"/data/local/tmp/asound.state\"",

8
alsactl/.gitignore vendored
View file

@ -1,3 +1,5 @@
alsa-store.service
alsa-restore.service
90-alsa-restore.rules
conf/90-alsa-restore.rules
conf/alsa-state.service
conf/alsa-store.service
conf/alsa-restore.service
conf/alsa-card-wait@.service

View file

@ -11,8 +11,9 @@ AM_CFLAGS = -D_GNU_SOURCE
AM_CPPFLAGS = -I$(top_srcdir)/include
alsactl_SOURCES=alsactl.c state.c lock.c utils.c init_parse.c init_ucm.c \
daemon.c monitor.c clean.c info.c
alsactl_SOURCES=alsactl.c state.c lock.c utils.c wait.c \
init_parse.c init_ucm.c boot_params.c \
daemon.c monitor.c clean.c info.c export.c
alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \
-DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \
@ -24,13 +25,14 @@ noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c \
init_utils_run.c init_sysfs.c
udevrules_DATA = \
90-alsa-restore.rules
conf/90-alsa-restore.rules
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
alsa-state.service \
alsa-restore.service
conf/alsa-state.service \
conf/alsa-restore.service \
conf/alsa-card-wait@.service
install-data-hook:
$(MKDIR_P) -m 0755 \
@ -45,6 +47,7 @@ endif
edit = \
extratest=$$(echo ' $(ALSACTL_UDEV_EXTRATEST)' | sed -e 's/__/ /g' -e 's/^ $$//'); \
args=$$(echo ' $(ALSACTL_UDEV_ARGS)' | sed -e 's/__/ /g' -e 's/^ $$//'); \
mkdir -p conf; \
$(SED) -r -e 's,@sbindir\@,$(sbindir),g' \
-e 's,@mydatadir\@,$(mydatadir),g' \
-e 's,@daemonswitch\@,$(ALSACTL_DAEMONSWITCH),g' \
@ -53,24 +56,29 @@ edit = \
-e "s;@args\@;$${args};g" \
< $< > $@ || rm $@
alsa-state.service: alsa-state.service.in
conf/alsa-state.service: conf/alsa-state.service.in
$(edit)
alsa-restore.service: alsa-restore.service.in
conf/alsa-restore.service: conf/alsa-restore.service.in
$(edit)
90-alsa-restore.rules: 90-alsa-restore.rules.in
conf/alsa-card-wait@.service: conf/alsa-card-wait@.service.in
$(edit)
conf/90-alsa-restore.rules: conf/90-alsa-restore.rules.in
$(edit)
EXTRA_DIST += \
alsa-state.service.in \
alsa-restore.service.in \
90-alsa-restore.rules.in
conf/alsa-state.service.in \
conf/alsa-restore.service.in \
conf/alsa-card-wait@.service.in \
conf/90-alsa-restore.rules.in
CLEANFILES = \
alsa-state.service \
alsa-restore.service \
90-alsa-restore.rules
conf/alsa-state.service \
conf/alsa-restore.service \
conf/alsa-card-wait@.service \
conf/90-alsa-restore.rules
%.7: %.xml
xmlto man $?

View file

@ -44,14 +44,19 @@
#ifndef SYS_LOCKPATH
#define SYS_LOCKPATH "/var/lock"
#endif
#ifndef SYS_CARD_GROUP
#define SYS_CARD_GROUP SYS_ASOUND_DIR "/card-group.state"
#endif
int debugflag = 0;
int force_restore = 1;
int ignore_nocards = 0;
int do_lock = 0;
int use_syslog = 0;
int do_export = 0;
char *command;
char *statefile = NULL;
char *groupfile = SYS_CARD_GROUP;
char *lockpath = SYS_LOCKPATH;
char *lockfile = SYS_LOCKFILE;
@ -78,6 +83,7 @@ static struct arg args[] = {
{ 'v', "version", "print version of this program" },
{ HEADER, NULL, "Available state options:" },
{ FILEARG | 'f', "file", "configuration file (default " SYS_ASOUNDRC ")" },
{ FILEARG | 'G', "group-file", "card group configuration file (default " SYS_CARD_GROUP ")" },
{ FILEARG | 'a', "config-dir", "boot / hotplug configuration directory (default " SYS_ASOUND_DIR ")" },
{ 'l', "lock", "use file locking to serialize concurrent access" },
{ 'L', "no-lock", "do not use file locking to serialize concurrent access" },
@ -92,6 +98,7 @@ static struct arg args[] = {
{ FILEARG | 'r', "runstate", "save restore and init state to this file (only errors)" },
{ 0, NULL, " default settings is 'no file set'" },
{ 'R', "remove", "remove runstate file at first, otherwise append errors" },
{ 'Y', "export", "export card state as key=value pairs (restore command only)" },
{ INTARG | 'p', "period", "store period in seconds for the daemon command" },
{ FILEARG | 'e', "pid-file", "pathname for the process id (daemon mode)" },
{ HEADER, NULL, "Available init options:" },
@ -105,6 +112,7 @@ static struct arg args[] = {
#ifdef HAVE_ALSA_USE_CASE_H
{ 'D', "ucm-defaults", "execute also the UCM 'defaults' section" },
{ 'U', "no-ucm", "don't init with UCM" },
{ 'm', "force-ucm-restore", "force UCM restore for boot card groups" },
#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
{ 'X', "ucm-nodev", "show UCM no device errors" },
#endif
@ -115,6 +123,7 @@ static struct arg args[] = {
{ CARDCMD, "restore", "load current driver setup for one or each soundcards" },
{ EMPCMD, NULL, " from configuration file" },
{ CARDCMD, "nrestore", "like restore, but notify the daemon to rescan soundcards" },
{ CARDCMD, "wrestore", "wait for card ready, then restore" },
{ CARDCMD, "init", "initialize driver to a default state" },
{ CARDCMD, "daemon", "store state periodically for one or each soundcards" },
{ CARDCMD, "rdaemon", "like daemon but do the state restore at first" },
@ -301,6 +310,9 @@ int main(int argc, char *argv[])
case 'f':
cfgfile = optarg;
break;
case 'G':
groupfile = optarg;
break;
case 'a':
cfgdir = optarg;
break;
@ -341,6 +353,9 @@ int main(int argc, char *argv[])
case 'U':
initflags |= FLAG_UCM_DISABLED;
break;
case 'm':
initflags |= FLAG_UCM_RESTORE;
break;
case 'X':
initflags |= FLAG_UCM_NODEV;
break;
@ -350,6 +365,9 @@ int main(int argc, char *argv[])
case 'R':
removestate = 1;
break;
case 'Y':
do_export = 1;
break;
case 'P':
force_restore = 0;
break;
@ -441,7 +459,11 @@ int main(int argc, char *argv[])
syslog(LOG_INFO, "alsactl " SND_UTIL_VERSION_STR " daemon started");
}
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
snd_lib_error_set_handler(error_handler);
#else
snd_lib_log_set_handler(log_handler);
#endif
if (!strcmp(cmd, "init")) {
res = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname);
@ -450,10 +472,15 @@ int main(int argc, char *argv[])
res = save_state(cfgfile, cardname);
} else if (!strcmp(cmd, "restore") ||
!strcmp(cmd, "rdaemon") ||
!strcmp(cmd, "nrestore")) {
!strcmp(cmd, "nrestore") ||
!strcmp(cmd, "wrestore")) {
if (removestate)
remove(statefile);
if (!strcmp(cmd, "wrestore"))
initflags |= FLAG_UCM_WAIT;
res = load_state(cfgdir, cfgfile, initfile, initflags, cardname, init_fallback);
if (do_export && res >= 0)
res = export_cards(cardname);
if (!strcmp(cmd, "rdaemon")) {
do_nice(use_nice, sched_idle);
res = state_daemon(cfgfile, cardname, period, pidfile);

View file

@ -2,14 +2,17 @@
#include <alsa/asoundlib.h>
#define LOCK_TIMEOUT 10
#define DEFAULT_SYNC_TIME 20
extern int debugflag;
extern int force_restore;
extern int ignore_nocards;
extern int do_lock;
extern int use_syslog;
extern int do_export;
extern char *command;
extern char *statefile;
extern char *groupfile;
extern char *lockpath;
extern char *lockfile;
@ -24,7 +27,8 @@ void info_(const char *fcn, long line, const char *fmt, ...);
void error_(const char *fcn, long line, const char *fmt, ...);
void cerror_(const char *fcn, long line, int cond, const char *fmt, ...);
void dbg_(const char *fcn, long line, const char *fmt, ...);
void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...);
void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...);
void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg);
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define info(...) do { info_(__func__, __LINE__, __VA_ARGS__); } while (0)
@ -43,6 +47,19 @@ void error_handler(const char *file, int line, const char *function, int err, co
#define FLAG_UCM_BOOT (1<<2)
#define FLAG_UCM_DEFAULTS (1<<3)
#define FLAG_UCM_NODEV (1<<4)
#define FLAG_UCM_RESTORE (1<<5)
#define FLAG_UCM_WAIT (1<<6)
enum {
CARD_STATE_WAIT = 1, /* skip configuration (wait for sync) */
CARD_STATE_SKIP = 2, /* skip card */
CARD_STATE_RESTORED = 3, /* card was restored */
};
static inline bool card_state_is_okay(int state)
{
return state >= CARD_STATE_WAIT && state <= CARD_STATE_RESTORED;
}
void snd_card_iterator_init(struct snd_card_iterator *iter, int cardno);
int snd_card_iterator_sinit(struct snd_card_iterator *iter, const char *cardname);
@ -52,14 +69,27 @@ int snd_card_iterator_error(struct snd_card_iterator *iter);
int load_configuration(const char *file, snd_config_t **top, int *open_failed);
int init(const char *cfgdir, const char *file, int flags, const char *cardname);
int init_ucm(int flags, int cardno);
bool validate_boot_time(long long boot_time, long long current_time, long long synctime);
int read_boot_params(snd_ctl_t *handle, long long *boot_time, long long *sync_time, long long *restore_time, long long *primary_card);
int write_boot_params(snd_ctl_t *handle, long long boot_time, long long sync_time, long long restore_time, long long primary_card);
int card_group_load(snd_config_t **config);
int card_group_save(snd_config_t *config);
int card_group_get_int64(snd_config_t *config_group, const char *id, long long *val);
int card_group_set_int64(snd_config_t *config_group, const char *id, long long val);
int check_boot_params_validity(snd_ctl_t *handle, int cardno, char **boot_card_group, bool *valid, bool *in_sync, bool *restored, int *primary_card, long long *synctime);
int update_boot_params(snd_ctl_t *handle, int cardno, const char *boot_card_group, bool valid, bool restored, long long synctime);
int boot_params_remove_card(int cardno);
int state_lock(const char *file, int timeout);
int state_unlock(int lock_fd, const char *file);
int card_lock(int card_number, int timeout);
int card_unlock(int lock_fd, int card_number);
int group_state_lock(const char *file, int timeout);
int group_state_unlock(int lock_fd, const char *file);
int save_state(const char *file, const char *cardname);
int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags,
const char *cardname, int do_init);
int wait_for_card(long long timeout, int cardno);
int power(const char *argv[], int argc);
int monitor(const char *name);
int general_info(const char *name);
@ -68,6 +98,12 @@ int state_daemon(const char *file, const char *cardname, int period,
int state_daemon_kill(const char *pidfile, const char *cmd);
int clean(const char *cardname, char *const *extra_args);
int snd_card_clean_cfgdir(const char *cfgdir, int cardno);
void add_linked_card(int cardno);
/* export */
int export_card_state_set(int card, int state);
int export_cards(const char *cardname);
/* utils */

1085
alsactl/boot_params.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@ LABEL="alsa_restore_go"
ENV{ALSA_CARD_NUMBER}="$attr{device/number}"
# mark HDA analog card; HDMI/DP card does not have capture devices
DRIVERS=="snd_hda_intel", TEST=="device/pcmC$env{ALSA_CARD_NUMBER}D0p", RUN+="/bin/sh -c 'echo ALSA_CARD_HDA_ANALOG=$env{ALSA_CARD_NUMBER} >> /run/udev/alsa-hda-analog-card'"
DRIVERS=="snd_hda_intel", TEST=="device/pcmC$env{ALSA_CARD_NUMBER}D0c", RUN+="/bin/sh -c 'echo ALSA_CARD_HDA_ANALOG=$env{ALSA_CARD_NUMBER} >> /run/udev/alsa-hda-analog-card'"
# check for ACP hardware
TEST=="device/device/acp3x-dmic-capture", GOTO="alsa_hda_analog"
@ -24,7 +24,8 @@ IMPORT{program}="/usr/bin/cat /run/udev/alsa-hda-analog-card"
ENV{ALSA_CARD_HDA_ANALOG}!="", ENV{ALSA_CARD_NUMBER}="$env{ALSA_CARD_HDA_ANALOG}"
LABEL="alsa_restore_std"
TEST!="@daemonswitch@", RUN+="@sbindir@/alsactl@args@ restore $env{ALSA_CARD_NUMBER}"
TEST=="@daemonswitch@", RUN+="@sbindir@/alsactl@args@ nrestore $env{ALSA_CARD_NUMBER}"
TEST!="@daemonswitch@", IMPORT{program}="@sbindir@/alsactl@args@ --export restore $env{ALSA_CARD_NUMBER}"
TEST=="@daemonswitch@", IMPORT{program}="@sbindir@/alsactl@args@ --export nrestore $env{ALSA_CARD_NUMBER}"
ENV{ALSA_CARD_STATE}=="waiting", ENV{SYSTEMD_WANTS}="alsa-card-wait@$env{ALSA_CARD_NUMBER}.service"
LABEL="alsa_restore_end"

View file

@ -0,0 +1,12 @@
#
# ALSA card initialization handler for cards in waiting state
# This service is triggered by udev when ALSA_CARD_STATE=waiting
#
[Unit]
Description=ALSA Card Initialization for card %I
[Service]
Type=oneshot
RemainAfterExit=no
ExecStart=@sbindir@/alsactl@args@ wrestore %i

133
alsactl/export.c Normal file
View file

@ -0,0 +1,133 @@
/*
* Advanced Linux Sound Architecture Control Program - Export
* Copyright (c) by Jaroslav Kysela <perex@perex.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* Export variable syntax:
*
* For single card:
*
* ALSA_CARD_NUMBER=<number>
* ALSA_CARD_STATE=<state>
*
* For multiple cards:
*
* ALSA_CARD#_STATE=<state> # where # is replaced with the card number
*
* State list:
*
* active # card was initialized and active
* skip # card was skipped (other card in group)
* waiting # card is waiting for the initial configuration
*/
#include "aconfig.h"
#include "version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alsactl.h"
/* Global array to store card states (0 = active, 1 = waiting) */
static int export_card_state[32];
/**
* export_card_state_set - Set card state
* @card: Card number
* @state: Card state (0 = active, 1 = waiting)
*
* Returns 0 on success, negative error code on failure
*/
int export_card_state_set(int card, int state)
{
/* Check bounds */
if (card < 0 || (unsigned long)card >= ARRAY_SIZE(export_card_state))
return -EINVAL;
export_card_state[card] = state;
return 0;
}
/**
* export_card_state_print - Export and print card state information
* @iter: Card iterator containing current card information
*
* Prints key=value pairs based on iterator's single flag and export_card_state array.
* Returns 0 on success, negative error code on failure
*/
static int export_card_state_print(struct snd_card_iterator *iter)
{
const char *state;
int istate;
if (!iter)
return -EINVAL;
/* Check bounds */
if (iter->card < 0 || (unsigned long)iter->card >= ARRAY_SIZE(export_card_state))
return -EINVAL;
/* Determine state from export_card_state array */
istate = export_card_state[iter->card];
switch (istate) {
case CARD_STATE_WAIT: state = "waiting"; break;
case CARD_STATE_SKIP: state = "skip"; break;
default: state = "active"; break;
}
if (iter->single) {
/* Single card export format */
printf("ALSA_CARD_NUMBER=%d\n", iter->card);
printf("ALSA_CARD_STATE=%s\n", state);
} else {
/* Multiple cards export format */
printf("ALSA_CARD%d_STATE=%s\n", iter->card, state);
}
return 0;
}
/**
* export_cards - Export state for all cards
* @cardname: Card name or NULL for all cards
*
* Returns 0 on success, negative error code on failure
*/
int export_cards(const char *cardname)
{
struct snd_card_iterator iter;
const char *name;
int ret;
ret = snd_card_iterator_sinit(&iter, cardname);
if (ret < 0)
return ret;
while ((name = snd_card_iterator_next(&iter)) != NULL) {
ret = export_card_state_print(&iter);
if (ret < 0)
break;
}
if (ret == 0)
ret = snd_card_iterator_error(&iter);
return ret;
}

View file

@ -1762,7 +1762,7 @@ int init(const char *cfgdir, const char *filename, int flags, const char *cardna
continue;
}
err = init_ucm(flags, iter.card);
if (err == 0)
if (err == 0 || card_state_is_okay(err))
continue;
err = init_space(&space, iter.card);
if (err != 0)

View file

@ -21,6 +21,8 @@
#include "aconfig.h"
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include "alsactl.h"
#ifdef HAVE_ALSA_USE_CASE_H
@ -28,45 +30,269 @@
#include <alsa/use-case.h>
/*
* Keep it as simple as possible. Execute commands from the
* FixedBootSequence and BootSequence only.
* Helper: Check if card should skip initialization based on boot parameters
* Returns: 1 if should skip, 2 if should skip other card, 0 if should continue, negative on error
* If check_restored is true, also checks if card state is already restored
*/
static int should_skip_initialization(snd_ctl_t *ctl, int cardno, int flags,
char **boot_card_group, bool *valid,
bool *in_sync, bool *restored, int *primary_card,
long long *synctime)
{
int err;
err = check_boot_params_validity(ctl, cardno, boot_card_group, valid, in_sync, restored, primary_card, synctime);
if (err < 0) {
dbg("boot parameters validity failed: %s", snd_strerror(err));
return err;
}
/* do nothing for other cards in group */
if (*valid && *primary_card != cardno) {
dbg("Skipping card %d - not primary (primary is %d)", cardno, *primary_card);
return CARD_STATE_SKIP;
}
/* for immediate initialization, caller must set UCM force-restore flag */
if (*valid && *in_sync && (flags & FLAG_UCM_RESTORE) == 0) {
dbg("Skipping card %d - in sync and no force-restore flag", cardno);
return CARD_STATE_WAIT;
}
return 0;
}
/*
* Helper: Get boot card group configuration from UCM
* Returns: 0 on success, negative on error
*/
static int get_boot_card_group_config(snd_use_case_mgr_t *uc_mgr, char **boot_card_group, long long *synctime)
{
char *sync_time = NULL;
int err;
err = snd_use_case_get(uc_mgr, "=BootCardGroup", (const char **)boot_card_group);
if (err != 0 || *boot_card_group == NULL) {
return -ENOENT;
}
dbg("BootCardGroup found: %s", *boot_card_group);
/* Get optional sync time */
err = snd_use_case_get(uc_mgr, "=BootCardSyncTime", (const char **)&sync_time);
if (err == 0 && sync_time != NULL) {
char *endptr;
errno = 0;
*synctime = strtoll(sync_time, &endptr, 10);
if (errno != 0 || *endptr != '\0' || endptr == sync_time) {
error("Invalid BootCardSyncTime value '%s'", sync_time);
*synctime = DEFAULT_SYNC_TIME;
}
free(sync_time);
}
return 0;
}
/*
* Helper: Open UCM manager with appropriate flags
* Returns: 0 on success, negative on error
*/
static int open_ucm_manager(snd_use_case_mgr_t **uc_mgr, int cardno, int flags,
bool valid, bool fixed_boot)
{
char id[64], *nodev, *in_boot;
int err;
nodev = (flags & FLAG_UCM_NODEV) ? "" : "-";
in_boot = (valid || !fixed_boot) ? "" : "<<<InBoot=1>>>";
snprintf(id, sizeof(id), "%s%shw:%d", nodev, in_boot, cardno);
err = snd_use_case_mgr_open(uc_mgr, id);
dbg("ucm open '%s': %d", id, err);
return err;
}
/*
* Helper: Reopen UCM manager without InBoot flag
* Returns: 0 on success, negative on error
*/
static int reopen_ucm_manager(snd_use_case_mgr_t **uc_mgr, int cardno, int flags)
{
char id[64], *nodev;
int err;
snd_use_case_mgr_close(*uc_mgr);
nodev = (flags & FLAG_UCM_NODEV) ? "" : "-";
snprintf(id, sizeof(id), "%shw:%d", nodev, cardno);
err = snd_use_case_mgr_open(uc_mgr, id);
dbg("ucm reopen '%s': %d", id, err);
return err;
}
/*
* Helper: Execute boot sequences
* Returns: 0 on success, negative on error
*/
static int execute_boot_sequences(snd_use_case_mgr_t *uc_mgr, int flags, bool fixed_boot)
{
int err = 0;
if (fixed_boot) {
err = snd_use_case_set(uc_mgr, "_fboot", NULL);
dbg("ucm _fboot: %d", err);
if (err == -ENOENT && (flags & FLAG_UCM_BOOT) != 0) {
/* _fboot not found but _boot requested - continue */
err = 0;
} else if (err < 0) {
return err;
}
}
if (flags & FLAG_UCM_BOOT) {
err = snd_use_case_set(uc_mgr, "_boot", NULL);
dbg("ucm _boot: %d", err);
if (err < 0)
return err;
if ((flags & FLAG_UCM_DEFAULTS) != 0)
err = snd_use_case_set(uc_mgr, "_defaults", NULL);
}
return err;
}
/*
* Execute commands from the FixedBootSequence and BootSequence.
* Handle also card groups.
* Returns: 0 = success, 1 = skip this card (e.g. linked or in-sync), negative on error
*/
int init_ucm(int flags, int cardno)
{
snd_use_case_mgr_t *uc_mgr;
char id[32], *nodev;
int err;
char id[64];
char *boot_card_group = NULL, *boot_card_group_verify = NULL;
bool fixed_boot, valid = false, in_sync = false, restored = false;
snd_ctl_t *ctl = NULL;
int err, primary_card = -1, lock_fd = -1;
long long synctime = -1;
if (flags & FLAG_UCM_DISABLED) {
dbg("ucm disabled");
return -ENXIO;
}
nodev = (flags & FLAG_UCM_NODEV) ? "" : "-";
snprintf(id, sizeof(id), "%shw:%d", nodev, cardno);
err = snd_use_case_mgr_open(&uc_mgr, id);
dbg("ucm open '%s': %d", id, err);
if (err < 0)
fixed_boot = (flags & FLAG_UCM_FBOOT) != 0;
snprintf(id, sizeof(id), "hw:%d", cardno);
err = snd_ctl_open(&ctl, id, 0);
if (err < 0) {
dbg("UCM: unable to open control device '%s': %s", id, snd_strerror(err));
return err;
if (flags & FLAG_UCM_FBOOT) {
err = snd_use_case_set(uc_mgr, "_fboot", NULL);
dbg("ucm _fboot: %d", err);
if (err == -ENOENT && (flags & FLAG_UCM_BOOT) != 0) {
/* nothing */
}
err = should_skip_initialization(ctl, cardno, flags, &boot_card_group, &valid,
&in_sync, &restored, &primary_card, &synctime);
if (err != 0)
goto _fin;
if (valid) {
if (restored) {
err = CARD_STATE_RESTORED;
goto _fin;
}
lock_fd = group_state_lock(groupfile, LOCK_TIMEOUT);
if (lock_fd < 0) {
err = lock_fd;
goto _fin;
}
}
err = open_ucm_manager(&uc_mgr, cardno, flags, valid, fixed_boot);
if (err < 0)
goto _fin;
if (!fixed_boot)
goto _execute_boot;
if (!valid) {
err = get_boot_card_group_config(uc_mgr, &boot_card_group, &synctime);
if (err == -ENOENT) {
/* No BootCardGroup - remove any existing boot params */
err = boot_params_remove_card(cardno);
if (err < 0)
goto _error;
goto _execute_boot;
} else if (err < 0) {
goto _error;
}
if (lock_fd < 0) {
lock_fd = group_state_lock(groupfile, LOCK_TIMEOUT);
if (lock_fd < 0) {
err = lock_fd;
goto _error;
}
}
err = should_skip_initialization(ctl, cardno, flags, &boot_card_group_verify,
&valid, &in_sync, &restored, &primary_card, &synctime);
if (err != 0)
goto _error;
if (valid && (boot_card_group_verify == NULL || strcmp(boot_card_group_verify, boot_card_group) != 0)) {
dbg("expected different boot card group (got '%s', expected '%s')", boot_card_group_verify, boot_card_group);
err = -EINVAL;
goto _error;
}
if ((flags & FLAG_UCM_RESTORE) == 0 && (!valid || restored)) {
dbg("Skipping card %d (group '%s') - %s and no force-restore flag", cardno, boot_card_group,
!valid ? "validity not passed" : "already restored");
if (!valid) {
/* create initial 'Boot' element */
err = update_boot_params(ctl, cardno, boot_card_group, 0, restored, synctime);
if (err < 0)
goto _error;
}
err = restored ? CARD_STATE_RESTORED : CARD_STATE_WAIT;
goto _error;
}
err = reopen_ucm_manager(&uc_mgr, cardno, flags);
if (err < 0)
goto _fin;
}
if (flags & FLAG_UCM_BOOT) {
err = snd_use_case_set(uc_mgr, "_boot", NULL);
dbg("ucm _boot: %d", err);
_execute_boot:
if (flags & FLAG_UCM_FBOOT)
restored = true;
if (boot_card_group) {
err = update_boot_params(ctl, cardno, boot_card_group, valid, restored, synctime);
if (err < 0)
goto _error;
if ((flags & FLAG_UCM_DEFAULTS) != 0)
err = snd_use_case_set(uc_mgr, "_defaults", NULL);
}
err = execute_boot_sequences(uc_mgr, flags, fixed_boot);
if (err < 0)
goto _error;
err = 0;
_error:
snd_use_case_mgr_close(uc_mgr);
_fin:
if (lock_fd >= 0)
group_state_unlock(lock_fd, groupfile);
if (ctl)
snd_ctl_close(ctl);
free(boot_card_group);
free(boot_card_group_verify);
dbg("ucm init complete %d", err);
return err;
}

View file

@ -181,6 +181,40 @@ int state_unlock(int _fd, const char *file)
return err;
}
static void group_state_lock_file(char *buf, size_t buflen)
{
const char *name = strrchr(groupfile, '/');
if (name && name[0])
name++;
else
name = "card-group.state";
snprintf(buf, buflen, "%s/%s.lock", lockpath, name);
}
int group_state_lock(const char *file, int timeout)
{
char fn[PATH_SIZE];
int err;
group_state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 1, timeout, -1);
if (err < 0)
error("file %s lock error: %s", file, strerror(-err));
return err;
}
int group_state_unlock(int _fd, const char *file)
{
char fn[PATH_SIZE];
int err;
group_state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 0, 10, _fd);
if (err < 0)
error("file %s unlock error: %s", file, strerror(-err));
return err;
}
static void card_lock_file(char *buf, size_t buflen, int card_number)
{
snprintf(buf, buflen, "%s/card%i.lock", lockpath, card_number);

View file

@ -176,7 +176,7 @@ static int check_control_cdev(int infd, bool *retry)
ssize_t len = read(infd, buf, sizeof(*ev) + NAME_MAX);
if (len < 0) {
if (errno != EAGAIN)
err = errno;
err = -errno;
break;
} else if (len == 0) {
break;

View file

@ -29,6 +29,33 @@
#include <errno.h>
#include "alsactl.h"
static int linked_cards[16];
static void init_linked_cards(void)
{
size_t index;
for (index = 0; index < ARRAY_SIZE(linked_cards); index++)
linked_cards[index] = -1;
}
void add_linked_card(int cardno)
{
size_t index;
for (index = 0; index < ARRAY_SIZE(linked_cards); index++) {
if (linked_cards[index] == cardno)
return;
}
for (index = 0; index < ARRAY_SIZE(linked_cards); index++) {
if (linked_cards[index] < 0) {
linked_cards[index] = cardno;
return;
}
}
error("Too many linked cards!");
}
static char *id_str(snd_ctl_elem_id_t *id)
{
@ -1511,7 +1538,7 @@ static int set_controls(int card, snd_config_t *top, int doit)
snd_ctl_elem_id_t *elem_id;
snd_config_t *control;
snd_config_iterator_t i, next;
int err, controls1 = -1, controls2 = -1;
int err, controls1 = -1, controls2 = -1, ucontrols = -1, diff;
unsigned int idx, count = 0;
char name[32], tmpid[16];
const char *id;
@ -1571,7 +1598,7 @@ static int set_controls(int card, snd_config_t *top, int doit)
count = snd_ctl_elem_list_get_count(list);
dbg("list count: %u", count);
if (count == 0)
goto _check;
goto _free;
snd_ctl_elem_list_set_offset(list, 0);
if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
error("No enough memory...");
@ -1581,13 +1608,15 @@ static int set_controls(int card, snd_config_t *top, int doit)
error("Cannot determine controls (2): %s", snd_strerror(err));
goto _free;
}
controls2 = 0;
/* skip non-readable elements */
controls2 = ucontrols = 0;
/* skip non-readable and count user elements */
for (idx = 0; idx < count; ++idx) {
snd_ctl_elem_info_clear(elem_info);
snd_ctl_elem_list_get_id(list, idx, elem_id);
snd_ctl_elem_info_set_id(elem_info, elem_id);
if (snd_ctl_elem_info(handle, elem_info) == 0) {
if (snd_ctl_elem_info_is_user(elem_info))
ucontrols++;
if (!snd_ctl_elem_info_is_readable(elem_info))
continue;
controls2++;
@ -1596,9 +1625,9 @@ static int set_controls(int card, snd_config_t *top, int doit)
/* check if we have additional controls in driver */
/* in this case we should go through init procedure */
_check:
dbg("controls1=%i controls2=%i", controls1, controls2);
if (controls1 >= 0 && controls1 != controls2) {
diff = controls2 - controls1;
dbg("controls1=%i controls2=%i ucontrols=%i diff=%i", controls1, controls2, ucontrols, diff);
if (controls1 >= 0 && (-diff > ucontrols || diff > ucontrols)) {
/* not very informative */
/* but value is used for check only */
err = -EAGAIN;
@ -1701,10 +1730,11 @@ int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags,
const char *cardname, int do_init)
{
int err, finalerr = 0, open_failed, lock_fd;
int err, finalerr = 0, open_failed, lock_fd, cardno;
struct snd_card_iterator iter;
snd_config_t *config;
const char *cardname1;
size_t index;
config = NULL;
err = load_configuration(file, &config, &open_failed);
@ -1721,6 +1751,9 @@ int load_state(const char *cfgdir, const char *file,
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
if (!do_init)
break;
init_linked_cards();
if (initflags & FLAG_UCM_WAIT)
wait_for_card(-1, iter.card);
lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
if (lock_fd < 0) {
finalerr = lock_fd;
@ -1729,9 +1762,12 @@ int load_state(const char *cfgdir, const char *file,
}
err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1);
card_unlock(lock_fd, iter.card);
if (card_state_is_okay(err))
export_card_state_set(iter.card, err);
if (err < 0) {
finalerr = err;
initfailed(iter.card, "init", err);
continue;
}
initfailed(iter.card, "restore", -ENOENT);
}
@ -1745,6 +1781,9 @@ int load_state(const char *cfgdir, const char *file,
if (err < 0)
goto out;
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
init_linked_cards();
if (initflags & FLAG_UCM_WAIT)
wait_for_card(-1, iter.card);
lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
if (lock_fd < 0) {
initfailed(iter.card, "lock", lock_fd);
@ -1752,7 +1791,12 @@ int load_state(const char *cfgdir, const char *file,
continue;
}
/* error is ignored */
init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
err = init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
/* return code 1 and 2 -> postpone initialization */
if (card_state_is_okay(err)) {
export_card_state_set(iter.card, err);
goto unlock_card;
}
/* do a check if controls matches state file */
if (do_init && set_controls(iter.card, config, 0)) {
err = init(cfgdir, initfile, initflags | FLAG_UCM_BOOT, cardname1);
@ -1766,6 +1810,18 @@ int load_state(const char *cfgdir, const char *file,
finalerr = err;
initfailed(iter.card, "restore", err);
}
/* for linked cards, restore controls, too */
for (index = 0; index < ARRAY_SIZE(linked_cards); index++) {
if ((cardno = linked_cards[index]) < 0)
break;
dbg("Restore for linked card %d", cardno);
if ((err = set_controls(cardno, config, 1))) {
if (!force_restore)
finalerr = err;
initfailed(cardno, "restore", err);
}
}
unlock_card:
card_unlock(lock_fd, iter.card);
}
err = finalerr ? finalerr : snd_card_iterator_error(&iter);

View file

@ -177,7 +177,7 @@ void dbg_(const char *fcn, long line, const char *fmt, ...)
va_end(ap);
}
void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...)
{
char buf[2048];
va_list arg;
@ -187,12 +187,36 @@ void error_handler(const char *file, int line, const char *function, int err, co
va_end(arg);
if (use_syslog)
syslog(LOG_ERR, "alsa-lib %s:%i:(%s) %s%s%s\n", file, line, function,
buf, err ? ": " : "", err ? snd_strerror(err) : "");
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
else
fprintf(stderr, "alsa-lib %s:%i:(%s) %s%s%s\n", file, line, function,
buf, err ? ": " : "", err ? snd_strerror(err) : "");
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
}
#if SND_LIB_VER(1, 2, 15) >= SND_LIB_VERSION
void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg)
{
char buf[2048], level[50] = "";
const char *text1, *text2;
if (!snd_lib_log_filter(prio, interface, NULL))
return;
text1 = snd_lib_log_priority(prio);
text2 = snd_lib_log_interface(interface);
if (text1 || text2)
snprintf(level, sizeof(level), "[%s.%s] ", text1 ? text1 : "", text2 ? text2 : "");
vsnprintf(buf, sizeof(buf), fmt, arg);
if (use_syslog)
syslog(LOG_ERR, "alsa-lib %s:%i:(%s) %s%s%s%s\n", file, line, function, level,
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
else
fprintf(stderr, "alsa-lib %s:%i:(%s) %s%s%s%s\n", file, line, function, level,
buf, errcode ? ": " : "", errcode ? snd_strerror(errcode) : "");
}
#endif
int load_configuration(const char *file, snd_config_t **top, int *open_failed)
{
snd_config_t *config;
@ -329,6 +353,7 @@ int snd_card_clean_cfgdir(const char *cfgdir, int cardno)
lasterr = -errno;
}
}
free(list);
return lasterr;
}

200
alsactl/wait.c Normal file
View file

@ -0,0 +1,200 @@
/*
* Advanced Linux Sound Architecture Control Program - Wait for Boot
* Copyright (c) by Jaroslav Kysela <perex@perex.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "aconfig.h"
#include "version.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <alsa/asoundlib.h>
#include "alsactl.h"
/**
* \brief Wait for card boot synchronization using Boot control element
* \param timeout Maximum wait time in seconds
* \param cardno Card number
* \return 0 on success, negative error code on failure
*
* This function waits until the card releases the 'waiting' state (UCM).
* It monitors the '.Boot' control element and uses snd_ctl_wait() and
* snd_ctl_read() for event-based waiting, similar to boot_wait() in
* ../alsa-lib/alsa-lib/src/ucm/main.c
*/
int wait_for_card(long long timeout, int cardno)
{
snd_ctl_t *handle;
snd_ctl_event_t *event;
snd_ctl_elem_id_t *id;
snd_ctl_elem_info_t *info;
snd_ctl_elem_value_t *value;
long long boot_time_val, restore_time_val;
long long synctime = -1;
struct timespec start_time, now;
char name[32];
int err;
sprintf(name, "hw:%d", cardno);
err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
if (err < 0) {
error("snd_ctl_open error for %s: %s", name, snd_strerror(err));
return err;
}
/* Try to get synctime from boot_synctime in group configuration */
if (timeout <= 0) {
bool valid = false, restored = false;
err = check_boot_params_validity(handle, cardno, NULL, &valid, NULL, &restored, NULL, &synctime);
if (err == 0 && synctime > 0) {
timeout = synctime;
dbg("Using boot_synctime value: %lld seconds", timeout);
} else {
timeout = DEFAULT_SYNC_TIME;
}
/* Break early if boot params are invalid or already restored */
if (!valid || restored) {
dbg("Boot params check: valid=%d, restored=%d - skipping wait", valid, restored);
snd_ctl_close(handle);
return 0;
}
}
snd_ctl_event_alloca(&event);
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_value_alloca(&value);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
snd_ctl_elem_id_set_name(id, ".Boot");
snd_ctl_elem_info_set_id(info, id);
err = snd_ctl_elem_info(handle, info);
if (err < 0) {
dbg("Boot control element not present on card %d, skipping wait", cardno);
snd_ctl_close(handle);
return 0; /* No Boot element, no wait needed */
}
if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_INTEGER64) {
error("Boot control element is not INTEGER64 type on card %d", cardno);
snd_ctl_close(handle);
return -EINVAL;
}
if (snd_ctl_elem_info_get_count(info) < 3) {
error("Boot control element does not have count >= 3 on card %d", cardno);
snd_ctl_close(handle);
return -EINVAL;
}
err = snd_ctl_subscribe_events(handle, 1);
if (err < 0) {
error("Cannot subscribe to control events: %s", snd_strerror(err));
snd_ctl_close(handle);
return err;
}
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
dbg("Waiting for card %d to become ready (timeout=%lld seconds)", cardno, timeout);
while (1) {
long long diff, remaining = 0;
long long sync_time_val = -1;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
/* Read current Boot control values */
err = read_boot_params(handle, &boot_time_val, &sync_time_val, &restore_time_val, NULL);
if (err < 0) {
error("Failed to read Boot control element: %s", snd_strerror(err));
goto _fin;
}
dbg("Boot info: boot_time=%lld, sync_time=%lld, restore_time=%lld", boot_time_val, sync_time_val, restore_time_val);
if (restore_time_val > 0) {
diff = now.tv_sec - restore_time_val;
dbg("Controls already restored (diff=%lld seconds), card is ready", diff);
err = 0;
goto _fin;
}
/* note that realtime may differ from monotonic time, add one second for safety */
diff = now.tv_sec - start_time.tv_sec;
if (diff > timeout + 1) {
dbg("Maximum wait time exceeded (%lld >= %lld seconds), proceeding", diff, timeout);
break;
}
remaining = timeout - diff;
/* Use synctime from element to limit timeout if available */
if (sync_time_val > 0 && sync_time_val < timeout) {
dbg("Limiting timeout from %lld to sync_time %lld seconds", timeout, sync_time_val);
timeout = sync_time_val;
}
if (!validate_boot_time(boot_time_val, now.tv_sec, timeout)) {
if (boot_time_val > 0) {
diff = now.tv_sec - boot_time_val;
dbg("Boot timeout reached (%lld >= %lld seconds), proceeding", diff, timeout);
}
break;
}
diff = now.tv_sec - boot_time_val;
if (timeout - diff < remaining)
remaining = timeout - diff;
if (remaining <= 0)
remaining = 1;
dbg("Waiting %lld seconds", remaining);
err = snd_ctl_wait(handle, remaining * 1000);
if (err < 0) {
error("snd_ctl_wait failed: %s", snd_strerror(err));
goto _fin;
}
if (err == 0)
continue; /* Timeout, no events */
/* Read and check events */
while (snd_ctl_read(handle, event) > 0) {
if (!(snd_ctl_event_elem_get_mask(event) & SND_CTL_EVENT_MASK_VALUE))
continue; /* Not a value change event */
if (snd_ctl_event_elem_get_interface(event) != SND_CTL_ELEM_IFACE_CARD ||
snd_ctl_event_elem_get_index(event) != 0 ||
strcmp(snd_ctl_event_elem_get_name(event), ".Boot") != 0)
continue;
dbg("Boot control element value changed");
break;
}
}
err = 0;
_fin:
snd_ctl_subscribe_events(handle, 0);
snd_ctl_close(handle);
return err;
}

View file

@ -34,6 +34,7 @@
static WINDOW *curses_initialized;
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
static void black_hole_error_handler(const char *file ATTRIBUTE_UNUSED,
int line ATTRIBUTE_UNUSED,
const char *function ATTRIBUTE_UNUSED,
@ -41,6 +42,18 @@ static void black_hole_error_handler(const char *file ATTRIBUTE_UNUSED,
const char *fmt ATTRIBUTE_UNUSED, ...)
{
}
#else
static void black_hole_log_handler(int prio ATTRIBUTE_UNUSED,
int interface ATTRIBUTE_UNUSED,
const char *file ATTRIBUTE_UNUSED,
int line ATTRIBUTE_UNUSED,
const char *function ATTRIBUTE_UNUSED,
int errcode ATTRIBUTE_UNUSED,
const char *fmt ATTRIBUTE_UNUSED,
va_list arg ATTRIBUTE_UNUSED)
{
}
#endif
void initialize_curses(bool use_color, bool use_mouse)
{
@ -55,7 +68,11 @@ void initialize_curses(bool use_color, bool use_mouse)
if (use_mouse)
mousemask(ALL_MOUSE_EVENTS, NULL);
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
snd_lib_error_set_handler(black_hole_error_handler);
#else
snd_lib_log_set_handler(black_hole_log_handler);
#endif
}
void app_shutdown(void)

View file

@ -52,6 +52,11 @@ Prints the current version.
.I \-l, \-\-list\-devices
Prints a list of all hardware MIDI ports.
.TP
.I \-x, \-\-list\-inactive
Use together with \fI\-l\fP option.
Print all MIDI ports including inactive ports.
.TP
.I \-L, \-\-list\-rawmidis
Prints all RawMIDI definitions.

View file

@ -57,6 +57,7 @@ static int stop;
static int sysex_interval;
static snd_rawmidi_t *input, **inputp;
static snd_rawmidi_t *output, **outputp;
static int list_all;
static void error(const char *format, ...)
{
@ -76,6 +77,7 @@ static void usage(void)
"-h, --help this help\n"
"-V, --version print current version\n"
"-l, --list-devices list all hardware ports\n"
"-x, --list-inactive list inactive ports, too\n"
"-L, --list-rawmidis list all RawMIDI definitions\n"
"-p, --port=name select port by name\n"
"-s, --send=file send the contents of a (.syx) file\n"
@ -151,6 +153,9 @@ static void list_device(snd_ctl_t *ctl, int card, int device)
card, device, sub, snd_strerror(err));
return;
}
if (!list_all &&
(snd_rawmidi_info_get_flags(info) & SNDRV_RAWMIDI_INFO_STREAM_INACTIVE))
continue;
name = snd_rawmidi_info_get_name(info);
sub_name = snd_rawmidi_info_get_subdevice_name(info);
if (sub == 0 && sub_name[0] == '\0') {
@ -471,11 +476,12 @@ static void add_send_hex_data(const char *str)
int main(int argc, char *argv[])
{
static const char short_options[] = "hVlLp:s:r:S::dt:aci:T:";
static const char short_options[] = "hVlxLp:s:r:S::dt:aci:T:";
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'},
{"list-devices", 0, NULL, 'l'},
{"list-inactive", 0, NULL, 'x'},
{"list-rawmidis", 0, NULL, 'L'},
{"port", 1, NULL, 'p'},
{"send", 1, NULL, 's'},
@ -508,6 +514,9 @@ int main(int argc, char *argv[])
case 'l':
do_device_list = 1;
break;
case 'x':
list_all = 1;
break;
case 'L':
do_rawmidi_list = 1;
break;

View file

@ -1464,6 +1464,13 @@ static void set_params(void)
chunk_size, buffer_size);
prg_exit(EXIT_FAILURE);
}
if (dump_hw_params) {
fprintf(stderr, _("HW Params of device \"%s\":\n"),
snd_pcm_name(handle));
fprintf(stderr, "--------------------\n");
snd_pcm_hw_params_dump(params, log);
fprintf(stderr, "--------------------\n");
}
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
error(_("Unable to get current sw params."));
@ -2703,7 +2710,7 @@ static void begin_wave(int fd, size_t cnt)
WaveHeader h;
WaveFmtBody f;
WaveChunkHeader cf, cd;
int bits;
int width, physical_width;
uint32_t tmp;
uint16_t tmp2;
@ -2711,23 +2718,22 @@ static void begin_wave(int fd, size_t cnt)
if (cnt == (size_t)-2)
cnt = 0x7fffff00;
bits = 8;
width = snd_pcm_format_physical_width(hwparams.format);
physical_width = snd_pcm_format_width(hwparams.format);
if (width < 0 || physical_width < 0)
goto _format;
switch ((unsigned long) hwparams.format) {
case SND_PCM_FORMAT_U8:
bits = 8;
break;
case SND_PCM_FORMAT_S16_LE:
bits = 16;
break;
case SND_PCM_FORMAT_S24_LE: /* S24_LE is 24 bits stored in 32 bit width with 8 bit padding */
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_FLOAT_LE:
bits = 32;
break;
case SND_PCM_FORMAT_S24_3LE:
bits = 24;
break;
default:
_format:
error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
prg_exit(EXIT_FAILURE);
}
@ -2745,17 +2751,11 @@ static void begin_wave(int fd, size_t cnt)
f.format = LE_SHORT(WAV_FMT_PCM);
f.channels = LE_SHORT(hwparams.channels);
f.sample_fq = LE_INT(hwparams.rate);
#if 0
tmp2 = (samplesize == 8) ? 1 : 2;
f.byte_p_spl = LE_SHORT(tmp2);
tmp = dsp_speed * hwparams.channels * (uint32_t) tmp2;
#else
tmp2 = hwparams.channels * snd_pcm_format_physical_width(hwparams.format) / 8;
tmp2 = hwparams.channels * physical_width / 8;
f.byte_p_spl = LE_SHORT(tmp2);
tmp = (uint32_t) tmp2 * hwparams.rate;
#endif
f.byte_p_sec = LE_INT(tmp);
f.bit_p_spl = LE_SHORT(bits);
f.bit_p_spl = LE_SHORT(width);
cd.type = WAV_DATA;
cd.length = LE_INT(cnt);

View file

@ -221,7 +221,17 @@ static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
period_time = buffer_time / DIV_BUFFERTIME;
/* Set buffer time and period time */
/* Set period time and buffer time */
err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
params, &period_time, 0);
if (err < 0) {
fprintf(bat->err, _("Set parameter to device error: "));
fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
period_time,
device_name, snd_strerror(err), err);
return err;
}
err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle,
params, &buffer_time, 0);
if (err < 0) {
@ -231,16 +241,6 @@ static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
device_name, snd_strerror(err), err);
return err;
}
err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
params, &period_time, 0);
if (err < 0) {
fprintf(bat->err, _("Set parameter to device error: "));
fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
period_time,
device_name, snd_strerror(err), err);
return err;
}
}
/* Write the parameters to the driver */

View file

@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(alsa-utils, 1.2.14)
AC_INIT(alsa-utils, 1.2.15.1)
AC_CONFIG_SRCDIR([aplay/aplay.c])
AC_PREFIX_DEFAULT(/usr)
AM_INIT_AUTOMAKE([subdir-objects])
@ -419,8 +419,8 @@ AC_ARG_WITH([systemdsystemunitdir],
if test "x$with_systemdsystemunitdir" != xno; then
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "$have_min_systemd" = "yes" \
-a -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" \
-a "x$with_systemdsystemunitdir" != xno ])
AC_ARG_WITH([asound-state-dir],
AS_HELP_STRING([--with-asound-state-dir=DIR], [Directory to place asound.state file in]),

View file

@ -141,7 +141,7 @@
#define PACKAGE_NAME "alsa-utils"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "alsa-utils 1.2.14"
#define PACKAGE_STRING "alsa-utils 1.2.15.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "alsa-utils"
@ -150,7 +150,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.2.14"
#define PACKAGE_VERSION "1.2.15.1"
/* directory containing sample data */
#define SOUNDSDIR "/vendor/usr/share/sounds/alsa"
@ -165,7 +165,7 @@
#define TIME_WITH_SYS_TIME 1
/* ALSA util version */
#define VERSION "1.2.14"
#define VERSION "1.2.15.1"
/* Define if FFADO library is available */
/* #undef WITH_FFADO */

View file

@ -4,6 +4,6 @@
#define SND_UTIL_MAJOR 1
#define SND_UTIL_MINOR 2
#define SND_UTIL_SUBMINOR 14
#define SND_UTIL_SUBMINOR 15
#define SND_UTIL_VERSION ((SND_UTIL_MAJOR<<16)|(SND_UTIL_MINOR<<8)|SND_UTIL_SUBMINOR)
#define SND_UTIL_VERSION_STR "1.2.14"
#define SND_UTIL_VERSION_STR "1.2.15.1"

View file

@ -31,11 +31,13 @@
static int show_all;
static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
static void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...)
{
va_list arg;
if (err == ENOENT) /* Ignore those misleading "warnings" */
if (errcode == ENOENT) /* Ignore those misleading "warnings" */
return;
va_start(arg, fmt);
fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
@ -45,6 +47,16 @@ static void error_handler(const char *file, int line, const char *function, int
putc('\n', stderr);
va_end(arg);
}
#else
static snd_lib_log_handler_t original_log_handler;
static void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg)
{
if (prio == SND_LOG_ERROR && errcode == ENOENT) /* Ignore those misleading "warnings" */
return;
if (original_log_handler)
original_log_handler(prio, interface, file, line, function, errcode, fmt, arg);
}
#endif
static void usage(void)
{
@ -356,7 +368,11 @@ int main(int argc, char **argv)
return 1;
}
#if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
snd_lib_error_set_handler(error_handler);
#else
original_log_handler = snd_lib_log_set_handler(log_handler);
#endif
switch (command) {
case LIST: