Files
third_party_alsa-utils/alsactl/boot_params.c
T

1086 lines
28 KiB
C

/*
* Advanced Linux Sound Architecture Control Program - Boot Parameters
* 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 <stddef.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include "alsactl.h"
/*
* Validate boot time
* Returns: true if boot_time is valid and within synchronization time, false otherwise
*/
bool validate_boot_time(long long boot_time, long long current_time, long long synctime)
{
long long diff;
if (boot_time <= 0)
return false;
diff = current_time - boot_time;
if (diff < 0) {
/* boot_time is in the future - invalid */
return false;
}
if (synctime > 0 && diff >= synctime) {
/* boot_time has exceeded timeout - invalid */
return false;
}
return true;
}
/*
* Read boot parameters from the '.Boot' control element
* Returns: 0 on success, negative error code on failure
*/
int read_boot_params(snd_ctl_t *handle, long long *boot_time, long long *sync_time,
long long *restore_time, long long *primary_card)
{
snd_ctl_elem_id_t *id;
snd_ctl_elem_value_t *value;
snd_ctl_elem_info_t *info;
int err;
if (boot_time)
*boot_time = -1;
if (sync_time)
*sync_time = -1;
if (restore_time)
*restore_time = -1;
if (primary_card)
*primary_card = -1;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_value_alloca(&value);
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
snd_ctl_elem_id_set_name(id, ".Boot");
snd_ctl_elem_id_set_index(id, 0);
snd_ctl_elem_info_set_id(info, id);
err = snd_ctl_elem_info(handle, info);
if (err < 0) {
if (err == -ENOENT)
return 0;
error("Cannot read '.Boot' control info: %s", snd_strerror(err));
return err;
}
if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_INTEGER64) {
error("'.Boot' control element is not of type INTEGER64");
return -EINVAL;
}
if (snd_ctl_elem_info_get_count(info) != 4) {
error("'.Boot' control element does not have 3 values");
return -EINVAL;
}
snd_ctl_elem_value_set_id(value, id);
err = snd_ctl_elem_read(handle, value);
if (err < 0) {
error("Cannot read '.Boot' control: %s", snd_strerror(err));
return err;
}
dbg("Read boot params: boot_time=%lld sync_time=%lld restore_time=%lld primary_card=%lld",
snd_ctl_elem_value_get_integer64(value, 0),
snd_ctl_elem_value_get_integer64(value, 1),
snd_ctl_elem_value_get_integer64(value, 2),
snd_ctl_elem_value_get_integer64(value, 3));
if (boot_time)
*boot_time = snd_ctl_elem_value_get_integer64(value, 0);
if (sync_time)
*sync_time = snd_ctl_elem_value_get_integer64(value, 1);
if (restore_time)
*restore_time = snd_ctl_elem_value_get_integer64(value, 2);
if (primary_card)
*primary_card = snd_ctl_elem_value_get_integer64(value, 3);
return 0;
}
/*
* Write boot parameters to the '.Boot' control element
* Returns: 0 on success, negative error code on failure
*/
int write_boot_params(snd_ctl_t *handle, long long boot_time, long long sync_time,
long long restore_time, long long primary_card)
{
snd_ctl_elem_id_t *id;
snd_ctl_elem_value_t *value;
snd_ctl_elem_info_t *info;
int err;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_value_alloca(&value);
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
snd_ctl_elem_id_set_name(id, ".Boot");
snd_ctl_elem_id_set_index(id, 0);
snd_ctl_elem_info_set_id(info, id);
err = snd_ctl_elem_info(handle, info);
if (err < 0) {
if (err == -ENOENT) {
/* Element not found, create a new user element with 3 integer64 values */
dbg("'.Boot' control not found, creating new user element");
/* Do not save this element to the state file */
snd_ctl_elem_info_set_inactive(info, 1);
snd_ctl_elem_info_set_read_write(info, 1, 1);
err = snd_ctl_add_integer64_elem_set(handle, info, 1, 4, -1, LLONG_MAX, 0);
if (err < 0) {
error("Cannot create '.Boot' user element: %s", snd_strerror(err));
return err;
}
/* Re-read the element info after creation */
err = snd_ctl_elem_info(handle, info);
if (err < 0) {
error("Cannot read '.Boot' control info after creation: %s", snd_strerror(err));
return err;
}
} else {
error("Cannot read '.Boot' control info: %s", snd_strerror(err));
return err;
}
}
dbg("Write boot params: boot_time=%lld sync_time=%lld restore_time=%lld primary_card=%lld",
boot_time, sync_time, restore_time, primary_card);
if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_INTEGER64) {
error("'.Boot' control element is not of type INTEGER64");
return -EINVAL;
}
if (snd_ctl_elem_info_get_count(info) != 4) {
error("'.Boot' control element does not have 3 values");
return -EINVAL;
}
snd_ctl_elem_value_set_id(value, id);
snd_ctl_elem_value_set_integer64(value, 0, boot_time);
snd_ctl_elem_value_set_integer64(value, 1, sync_time);
snd_ctl_elem_value_set_integer64(value, 2, restore_time);
snd_ctl_elem_value_set_integer64(value, 3, primary_card);
err = snd_ctl_elem_write(handle, value);
if (err < 0) {
error("Cannot write '.Boot' control: %s", snd_strerror(err));
return err;
}
return 0;
}
/*
* Structure for the group configuration file:
*
* <GROUP_NAME> {
* card.0 <int> # primary card in group
* card.1 <int> # optional - next card in group
* card.2 <int> # optional - next card in group
* boot_realtime <int64> # boot time (CLOCK_REALTIME) in seconds
* boot_last_update <int64> # timestamp of last configuration update (CLOCK_REALTIME) in seconds
* boot_monotonic <int64> # boot time (CLOCK_MONOTONIC_RAW) in seconds
* boot_synctime <int64> # synchronization time window in seconds
* }
*/
/*
* Read card group configuration from file
* Returns: 0 on success, negative error code on failure
*/
int card_group_load(snd_config_t **config)
{
snd_input_t *in;
int err;
if (!config)
return -EINVAL;
*config = NULL;
err = snd_config_top(config);
if (err < 0) {
error("Cannot create top config: %s", snd_strerror(err));
return err;
}
err = snd_input_stdio_open(&in, groupfile, "r");
if (err < 0) {
if (err == -ENOENT) {
dbg("Card group file '%s' not found", groupfile);
return 0;
}
error("Cannot open card group file '%s' for reading: %s", groupfile, snd_strerror(err));
goto _err;
}
err = snd_config_load(*config, in);
snd_input_close(in);
if (err < 0) {
error("Cannot load card group file '%s': %s", groupfile, snd_strerror(err));
goto _err;
}
return 0;
_err:
snd_config_delete(*config);
*config = NULL;
return err;
}
/*
* Write card group configuration to file
* Returns: 0 on success, negative error code on failure
*/
int card_group_save(snd_config_t *config)
{
snd_output_t *out;
char temp_file[PATH_MAX];
int err;
if (!config)
return -EINVAL;
snprintf(temp_file, sizeof(temp_file), "%s.new", groupfile);
err = snd_output_stdio_open(&out, temp_file, "w");
if (err < 0) {
error("Cannot open temporary card group file '%s' for writing: %s", temp_file, snd_strerror(err));
return err;
}
err = snd_config_save(config, out);
snd_output_close(out);
if (err < 0) {
error("Cannot save temporary card group file '%s': %s", temp_file, snd_strerror(err));
return err;
}
err = rename(temp_file, groupfile);
if (err < 0) {
err = -errno;
error("Cannot rename temporary card group file '%s' to '%s': %s", temp_file, groupfile, strerror(errno));
return err;
}
return 0;
}
/*
* Get int64 value from card group configuration
* Returns: 0 on success, negative error code on failure
*/
int card_group_get_int64(snd_config_t *config_group, const char *id, long long *val)
{
snd_config_t *node;
int err;
if (!config_group || !id || !val)
return -EINVAL;
err = snd_config_search(config_group, id, &node);
if (err < 0)
return err;
err = snd_config_get_integer64(node, val);
if (err < 0) {
long ival;
err = snd_config_get_integer(node, &ival);
if (err < 0)
return err;
*val = ival;
}
return 0;
}
/*
* Set int64 value in card group configuration
* Returns: 0 on success, negative error code on failure
*/
int card_group_set_int64(snd_config_t *config_group, const char *id, long long val)
{
snd_config_t *node;
int err;
if (!config_group || !id)
return -EINVAL;
err = snd_config_search(config_group, id, &node);
if (err < 0) {
_create:
err = snd_config_make_integer64(&node, id);
if (err < 0) {
error("Cannot create int64 node for id '%s': %s", id, snd_strerror(err));
return err;
}
err = snd_config_add(config_group, node);
if (err < 0) {
error("Cannot add int64 node for id '%s': %s", id, snd_strerror(err));
snd_config_delete(node);
return err;
}
} else {
/* alsa-lib should implement automatic type conversion */
if (snd_config_get_type(node) == SND_CONFIG_TYPE_INTEGER) {
snd_config_delete(node);
goto _create;
}
}
err = snd_config_set_integer64(node, val);
if (err < 0) {
error("Cannot set int64 value for id '%s': %s", id, snd_strerror(err));
return err;
}
return 0;
}
/*
* Helper: Find or create card compound within a group
* Returns: 0 on success, negative error code on failure
*/
static int card_group_get_or_create_card_compound(snd_config_t *config_group, snd_config_t **card_compound)
{
int err;
if (!config_group || !card_compound)
return -EINVAL;
err = snd_config_search(config_group, "card", card_compound);
if (err < 0) {
/* Create card compound */
err = snd_config_make_compound(card_compound, "card", 0);
if (err < 0) {
error("Cannot create card compound: %s", snd_strerror(err));
return err;
}
err = snd_config_add(config_group, *card_compound);
if (err < 0) {
error("Cannot add card compound: %s", snd_strerror(err));
snd_config_delete(*card_compound);
return err;
}
}
return 0;
}
/*
* Helper: Determine the primary card in the card compound
* Returns: card number, otherwise error code
*/
static long card_group_find_primary(snd_config_t *card_compound)
{
snd_config_iterator_t i, next;
if (!card_compound)
return -EINVAL;
if (snd_config_get_type(card_compound) != SND_CONFIG_TYPE_COMPOUND)
return -EINVAL;
snd_config_for_each(i, next, card_compound) {
snd_config_t *card_node = snd_config_iterator_entry(i);
long card_val;
int err;
err = snd_config_get_integer(card_node, &card_val);
if (err < 0)
return -EINVAL;
return card_val;
}
return -ENOENT;
}
/*
* Helper: Find card node in card compound
* Returns: card node if found, NULL otherwise
*/
static snd_config_t *card_group_find_card_node(snd_config_t *card_compound, int cardno)
{
snd_config_iterator_t i, next;
if (!card_compound)
return NULL;
if (snd_config_get_type(card_compound) != SND_CONFIG_TYPE_COMPOUND)
return NULL;
snd_config_for_each(i, next, card_compound) {
snd_config_t *card_node = snd_config_iterator_entry(i);
long card_val;
int err;
err = snd_config_get_integer(card_node, &card_val);
if (err < 0)
continue;
if ((int)card_val == cardno)
return card_node;
}
return NULL;
}
/*
* Helper: Add card to card compound
* Returns: 0 on success, negative error code on failure
*/
static int card_group_add_card(snd_config_t *card_compound, int cardno)
{
snd_config_t *new_card_node;
char card_id[16];
int card_index = 0;
int err;
if (!card_compound)
return -EINVAL;
/* Find next available card index */
while (card_index < 100) {
snprintf(card_id, sizeof(card_id), "%d", card_index);
if (snd_config_search(card_compound, card_id, &new_card_node) < 0)
break;
card_index++;
}
err = snd_config_make_integer(&new_card_node, card_id);
if (err < 0) {
error("Cannot create card node: %s", snd_strerror(err));
return err;
}
err = snd_config_set_integer(new_card_node, cardno);
if (err < 0) {
error("Cannot set card number: %s", snd_strerror(err));
snd_config_delete(new_card_node);
return err;
}
err = snd_config_add(card_compound, new_card_node);
if (err < 0) {
error("Cannot add card node: %s", snd_strerror(err));
snd_config_delete(new_card_node);
return err;
}
return 0;
}
/*
* Check boot parameters validity
* Returns: 0 on success, negative error code on failure
*/
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)
{
long long boot_time = -1;
long long restore_time = -1;
long long primary_card_val = -1;
long long boot_synctime = -1;
snd_config_t *config = NULL;
snd_config_t *config_group = NULL;
const char *card_group_name = NULL;
long long group_boot_realtime = -1;
long long group_boot_monotonic = -1;
long long group_boot_synctime = -1;
struct timespec ts_realtime, ts_monotonic;
long long diff_realtime, diff_monotonic, diff;
snd_config_iterator_t i, next;
int err = 0;
bool is_valid = false;
if (valid)
*valid = false;
if (in_sync)
*in_sync = false;
if (restored)
*restored = false;
if (primary_card)
*primary_card = -1;
if (boot_card_group)
*boot_card_group = NULL;
err = read_boot_params(handle, &boot_time, &boot_synctime, &restore_time, &primary_card_val);
if (err < 0) {
dbg("Boot element not present or error reading: %s", snd_strerror(err));
err = 0;
goto out;
}
if (boot_time <= 0) {
dbg("boot_time is not greater than zero: %lld", boot_time);
goto out;
}
err = card_group_load(&config);
if (err < 0) {
dbg("Error loading card group configuration: %s", snd_strerror(err));
err = 0;
goto out;
}
if (!config) {
dbg("No group configuration found");
goto out;
}
/* Find the card number in card groups - prefer group with newest boot_realtime */
snd_config_for_each(i, next, config) {
snd_config_t *n = snd_config_iterator_entry(i);
snd_config_t *card_compound;
const char *group_id;
long long current_boot_realtime;
if (snd_config_get_id(n, &group_id) < 0)
continue;
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
continue;
err = snd_config_search(n, "card", &card_compound);
if (err < 0)
continue;
if (!card_group_find_card_node(card_compound, cardno))
continue;
err = card_group_get_int64(n, "boot_realtime", &current_boot_realtime);
if (err < 0) {
dbg("boot_realtime not found in group '%s', skipping", group_id);
continue;
}
if (current_boot_realtime > group_boot_realtime) {
group_boot_realtime = current_boot_realtime;
config_group = n;
card_group_name = group_id;
}
}
if (!card_group_name) {
dbg("Card %d not found in any group configuration", cardno);
err = 0;
goto out;
}
err = card_group_get_int64(config_group, "boot_monotonic", &group_boot_monotonic);
if (err < 0) {
dbg("boot_monotonic not found in group '%s'", card_group_name);
err = 0;
goto out;
}
err = card_group_get_int64(config_group, "boot_synctime", &group_boot_synctime);
if (err < 0) {
dbg("boot_synctime not found in group '%s'", card_group_name);
err = 0;
goto out;
}
if (*synctime > 0 && group_boot_synctime != *synctime) {
err = -EINVAL;
error("Synchronization time window does not match (%lld != %lld)", *synctime, group_boot_synctime);
goto out;
}
if (boot_synctime > 0 && group_boot_synctime != boot_synctime) {
err = -EINVAL;
error("Element synchronization time window does not match (%lld != %lld)", boot_synctime, group_boot_synctime);
goto out;
}
if (clock_gettime(CLOCK_REALTIME, &ts_realtime) < 0) {
err = -errno;
error("Failed to get CLOCK_REALTIME: %s", strerror(errno));
goto out;
}
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts_monotonic) < 0) {
err = -errno;
error("Failed to get CLOCK_MONOTONIC_RAW: %s", strerror(errno));
goto out;
}
diff_monotonic = ts_monotonic.tv_sec - group_boot_monotonic;
diff_realtime = ts_realtime.tv_sec - group_boot_realtime;
diff = diff_realtime - diff_monotonic;
dbg("Card group '%s' sync diffs - %lld, %lld, %lld",
card_group_name, (long long)diff_monotonic, (long long)diff_realtime, (long long)diff);
/* if the time difference is too big (30 seconds) - obsolete configuration */
is_valid = diff < 30 || diff > -30;
if (valid)
*valid = is_valid;
if (is_valid) {
if (boot_card_group) {
*boot_card_group = strdup(card_group_name);
if (!*boot_card_group) {
err = -ENOMEM;
goto out;
}
}
if (primary_card) {
*primary_card = (int)primary_card_val;
dbg("Card group '%s' primary_card %d", card_group_name, *primary_card);
}
if (restored) {
*restored = restore_time > 0;
dbg("Card group '%s' restored %d", card_group_name, *restored);
}
if (in_sync) {
*in_sync = ts_realtime.tv_sec - group_boot_realtime < group_boot_synctime;
dbg("Card group '%s' in_sync %d - %lld, %lld, %lld",
card_group_name, *in_sync, (long long)ts_realtime.tv_sec,
(long long)group_boot_realtime, (long long)group_boot_synctime);
}
if (synctime)
*synctime = group_boot_synctime;
}
out:
if (config)
snd_config_delete(config);
return err;
}
/*
* Remove card from boot parameters - scans all groups and removes all invalid
* cards in group containing the card.
* Returns: 0 = no change, 1 = change (card(s) removed), negative error code on failure
*/
static int boot_params_remove_card_config(snd_config_t *group_config, int cardno)
{
snd_config_t *card_compound, *card_node;
snd_config_iterator_t i, next;
const char *group_id;
struct timespec ts_monotonic = {0};
long primary_card, card_val;
int err, changes;
bool valid;
if (snd_config_get_id(group_config, &group_id) < 0)
return -EINVAL;
err = snd_config_search(group_config, "card", &card_compound);
if (err < 0)
return 0;
primary_card = card_group_find_primary(card_compound);
if (primary_card == cardno) {
_primary:
dbg("Removing group '%s' (primary card %d)", group_id, cardno);
snd_config_delete(group_config);
return 1;
}
card_node = card_group_find_card_node(card_compound, cardno);
if (card_node == NULL)
return 0;
dbg("Removing card %d in group '%s'", cardno, group_id);
changes = 1;
snd_config_delete(card_node);
_retry:
snd_config_for_each(i, next, card_compound) {
snd_config_t *card_node = snd_config_iterator_entry(i);
snd_ctl_t *handle = NULL;
char name[32];
long long boot_time = -1;
err = snd_config_get_integer(card_node, &card_val);
if (err < 0)
continue;
valid = false;
sprintf(name, "hw:%ld", card_val);
err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
if (err >= 0) {
err = read_boot_params(handle, &boot_time, NULL, NULL, NULL);
snd_ctl_close(handle);
if (err < 0) {
dbg("Unable to read boot params for card %ld: %s", card_val, snd_strerror(err));
continue;
}
if (ts_monotonic.tv_sec == 0) {
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts_monotonic) < 0) {
dbg("Failed to get CLOCK_MONOTONIC_RAW: %s", strerror(errno));
return changes;
}
}
valid = validate_boot_time(boot_time, ts_monotonic.tv_sec, 0);
} else {
dbg("Unable to open ctl handle for card %ld: %s", card_val, snd_strerror(err));
}
if (!valid) {
if (card_val == primary_card) {
dbg("Primary card %ld is invalid in group '%s'", card_val, group_id);
goto _primary;
}
changes++;
dbg("Removing another card %ld in group '%s'", card_val, group_id);
snd_config_delete(card_node);
goto _retry;
}
}
if (snd_config_is_empty(card_compound)) {
dbg("No other cards in group '%s', removing", group_id);
snd_config_delete(group_config);
}
return changes > 0;
}
/*
* Remove card from boot parameters - scans all groups and removes all invalid
* cards in group containing the card.
* Returns: 0 on success, negative error code on failure
*/
int boot_params_remove_card(int cardno)
{
snd_config_t *config = NULL;
snd_config_iterator_t i, next;
const char *group_id = NULL;
int groups_changed = 0;
int err = 0;
/* Load the group configuration */
err = card_group_load(&config);
if (err < 0) {
error("Error loading card group configuration: %s", snd_strerror(err));
goto out;
}
if (!config) {
dbg("No group configuration found");
err = 0;
goto out;
}
/* Scan all groups and remove any that contain this card */
restart_scan:
snd_config_for_each(i, next, config) {
snd_config_t *group = snd_config_iterator_entry(i);
if (snd_config_get_id(group, &group_id) < 0)
continue;
if (snd_config_get_type(group) != SND_CONFIG_TYPE_COMPOUND)
continue;
err = boot_params_remove_card_config(group, cardno);
if (err < 0) {
error("Unable to remove card %d from group '%s': %s", cardno, group_id, snd_strerror(err));
continue;
}
if (err > 0)
groups_changed++;
goto restart_scan;
}
if (groups_changed == 0) {
dbg("Card %d not found in any group", cardno);
err = 0;
goto out;
}
dbg("Update %d group(s) containing card %d", groups_changed, cardno);
/* Save the updated configuration */
err = card_group_save(config);
if (err < 0) {
error("Cannot save card group configuration: %s", snd_strerror(err));
goto out;
}
out:
if (config)
snd_config_delete(config);
return err;
}
/*
* Update restored time for all cards in boot group
* cards in group containing the card.
*/
static void boot_params_update_restored(snd_config_t *card_compound, int skip_cardno,
long long boot_time, long long restored, long long primary_cardno)
{
snd_config_iterator_t i, next;
int err;
/* Scan all groups and remove any that contain this card */
snd_config_for_each(i, next, card_compound) {
snd_config_t *card_node = snd_config_iterator_entry(i);
long long boot_time_val, boot_synctime, boot_primary;
snd_ctl_t *handle;
char name[32];
long card_val;
err = snd_config_get_integer(card_node, &card_val);
if (err < 0)
continue;
if (skip_cardno == (long)card_val)
continue;
sprintf(name, "hw:%ld", card_val);
err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
if (err < 0) {
dbg("Unable to open ctl handle for card %ld: %s", card_val, snd_strerror(err));
continue;
}
err = read_boot_params(handle, &boot_time_val, &boot_synctime, NULL, &boot_primary);
if (err < 0) {
dbg("Unable to read boot params for card %ld: %s", card_val, snd_strerror(err));
goto _next;
}
if (boot_time_val != boot_time) {
dbg("Boot time mismatch (%lld != %lld)", boot_time, boot_time_val);
goto _next;
}
if (boot_primary != primary_cardno) {
dbg("Primary card mismatch (%lld != %lld)", boot_primary, primary_cardno);
goto _next;
}
err = write_boot_params(handle, boot_time_val, boot_synctime, restored, boot_primary);
if (err < 0) {
dbg("Unable to save boot params: %s", snd_strerror(err));
goto _next;
}
add_linked_card(card_val);
_next:
snd_ctl_close(handle);
}
}
/*
* Update boot parameters
* Returns: 0 on success, negative error code on failure
*/
int update_boot_params(snd_ctl_t *handle, int cardno, const char *boot_card_group,
bool valid, bool restored, long long synctime)
{
snd_config_t *config = NULL;
snd_config_t *config_group = NULL;
snd_config_t *card_compound = NULL;
struct timespec ts_realtime, ts_monotonic;
long long value;
long long boot_time = 0;
long long restore_time;
long long primary_card;
int err = 0;
if (!boot_card_group) {
error("boot_card_group parameter is required");
return -EINVAL;
}
if (synctime <= 0) {
error("synchronization time window is required");
return -EINVAL;
}
err = card_group_load(&config);
if (err < 0) {
error("Error loading card group configuration: %s", snd_strerror(err));
goto out;
}
if (!config) {
err = snd_config_top(&config);
if (err < 0) {
error("Cannot create top config: %s", snd_strerror(err));
goto out;
}
}
/* If valid is false, remove the boot_card_group from configuration */
if (!valid) {
err = snd_config_search(config, boot_card_group, &config_group);
if (err == 0 && config_group) {
err = boot_params_remove_card_config(config_group, cardno);
if (err < 0) {
error("Cannot manage group '%s': %s", boot_card_group, snd_strerror(err));
goto out;
}
dbg("Updated group '%s' in configuration", boot_card_group);
}
}
if (clock_gettime(CLOCK_REALTIME, &ts_realtime) < 0) {
err = -errno;
error("Failed to get CLOCK_REALTIME: %s", strerror(errno));
goto out;
}
ts_realtime.tv_nsec = 0;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts_monotonic) < 0) {
err = -errno;
error("Failed to get CLOCK_MONOTONIC_RAW: %s", strerror(errno));
goto out;
}
ts_monotonic.tv_nsec = 0;
restore_time = ts_monotonic.tv_sec;
err = snd_config_search(config, boot_card_group, &config_group);
if (err < 0) {
err = snd_config_make_compound(&config_group, boot_card_group, 0);
if (err < 0) {
error("Cannot create group '%s': %s", boot_card_group, snd_strerror(err));
goto out;
}
err = snd_config_add(config, config_group);
if (err < 0) {
error("Cannot add group '%s': %s", boot_card_group, snd_strerror(err));
snd_config_delete(config_group);
goto out;
}
}
err = card_group_get_or_create_card_compound(config_group, &card_compound);
if (err < 0)
goto out;
primary_card = card_group_find_primary(card_compound);
if (primary_card < 0) {
dbg("Primary card not found, using %d", cardno);
primary_card = cardno;
}
if (!card_group_find_card_node(card_compound, cardno)) {
err = card_group_add_card(card_compound, cardno);
if (err < 0)
goto out;
}
if (primary_card != cardno || valid) {
err = card_group_get_int64(config_group, "boot_realtime", &value);
if (err < 0) {
err = card_group_set_int64(config_group, "boot_realtime", ts_realtime.tv_sec);
if (err < 0) {
error("Cannot set boot_realtime: %s", snd_strerror(err));
goto out;
}
dbg("Set boot_realtime to %lld", (long long)ts_realtime.tv_sec);
} else {
dbg("Preserving existing boot_realtime: %lld", value);
ts_realtime.tv_sec = value;
}
err = card_group_get_int64(config_group, "boot_monotonic", &value);
if (err < 0) {
err = card_group_set_int64(config_group, "boot_monotonic", ts_monotonic.tv_sec);
if (err < 0) {
error("Cannot set boot_monotonic: %s", snd_strerror(err));
goto out;
}
dbg("Set boot_monotonic to %lld", (long long)ts_monotonic.tv_sec);
} else {
dbg("Preserving existing boot_monotonic: %lld", value);
ts_monotonic.tv_sec = value;
}
} else {
err = card_group_set_int64(config_group, "boot_realtime", ts_realtime.tv_sec);
if (err < 0) {
error("Cannot set boot_realtime: %s", snd_strerror(err));
goto out;
}
dbg("Set boot_realtime to %lld", (long long)ts_realtime.tv_sec);
err = card_group_set_int64(config_group, "boot_monotonic", ts_monotonic.tv_sec);
if (err < 0) {
error("Cannot set boot_monotonic: %s", snd_strerror(err));
goto out;
}
dbg("Set boot_monotonic to %lld", (long long)ts_monotonic.tv_sec);
}
if (synctime > 0) {
err = card_group_set_int64(config_group, "boot_synctime", synctime);
if (err < 0) {
error("Cannot set boot_synctime: %s", snd_strerror(err));
goto out;
}
}
err = card_group_set_int64(config_group, "boot_last_update", ts_realtime.tv_sec);
if (err < 0) {
error("Cannot set boot_last_update: %s", snd_strerror(err));
goto out;
}
err = card_group_save(config);
if (err < 0) {
error("Cannot save card group configuration: %s", snd_strerror(err));
goto out;
}
/* Update '.Boot' control element on the card */
boot_time = ts_monotonic.tv_sec;
if (!restored)
restore_time = -1;
err = write_boot_params(handle, boot_time, synctime, restore_time, primary_card);
if (err < 0) {
error("Cannot write boot parameters: %s", snd_strerror(err));
goto out;
}
if (primary_card == cardno)
boot_params_update_restored(card_compound, primary_card, boot_time, restore_time, primary_card);
dbg("Updated boot parameters for card %d in group '%s'", cardno, boot_card_group);
out:
if (config)
snd_config_delete(config);
return err;
}