mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
lszdev must be modified to share the site-specific configuration information from the persistent settings. The persistent configuration array will be read and tabularized based on the site-id input from the user. This patch also accommodates the site specific information in the lszdev --info command. New information about the site-ids are integrated with the command. For example, DEVICE dasd-eckd 0.0.f001 Names : - Modules : dasd_eckd_mod dasd_mod Online : no Exists : yes Persistent : yes Sites : 1,2 ATTRIBUTE ACTIVE PERSISTENT SITE1 SITE2 cmb_enable "0" "0" "1" - eer_enabled "0" - - - erplog "0" - - - failfast "0" - - - last_known_reservation_state "none" - - - online "0" "1" "1" "1" raw_track_access "0" - - - readonly "0" - - - reservation_policy "ignore" - - - use_diag "0" - - - Each available site specific configuration will be shown as column. The "Sites" parameter in the above output indicates the available site configurations and the SITE1 and SITE2 colums indicates the detailed configuration for the respective sites. Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
727 lines
18 KiB
C
727 lines
18 KiB
C
/*
|
|
* zdev - Modify and display the persistent configuration of devices
|
|
*
|
|
* Copyright IBM Corp. 2016, 2019
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "lib/util_path.h"
|
|
|
|
#include "attrib.h"
|
|
#include "device.h"
|
|
#include "devtype.h"
|
|
#include "internal.h"
|
|
#include "misc.h"
|
|
#include "namespace.h"
|
|
#include "setting.h"
|
|
#include "subtype.h"
|
|
#include "udev.h"
|
|
|
|
/* Create and initialize a new device. */
|
|
struct device *device_new(struct subtype *st, const char *id)
|
|
{
|
|
struct device *dev;
|
|
struct namespace *ns = st->namespace;
|
|
int i;
|
|
|
|
dev = misc_malloc(sizeof(struct device));
|
|
dev->subtype = st;
|
|
|
|
dev->id = ns->normalize_id(id);
|
|
dev->devid = ns->parse_id(id, 0);
|
|
if (!dev->id || !dev->devid) {
|
|
device_free(dev);
|
|
return NULL;
|
|
}
|
|
|
|
dev->active.settings = setting_list_new();
|
|
dev->persistent.settings = setting_list_new();
|
|
dev->autoconf.settings = setting_list_new();
|
|
|
|
for (i = 0; i < NUM_SITES; i++)
|
|
dev->site_specific[i].settings = setting_list_new();
|
|
|
|
return dev;
|
|
}
|
|
|
|
/* Release all resources associated with the specified device. */
|
|
void device_free(struct device *dev)
|
|
{
|
|
int i;
|
|
|
|
if (!dev)
|
|
return;
|
|
free(dev->id);
|
|
free(dev->devid);
|
|
setting_list_free(dev->active.settings);
|
|
setting_list_free(dev->persistent.settings);
|
|
setting_list_free(dev->autoconf.settings);
|
|
|
|
for (i = 0; i < NUM_SITES; i++)
|
|
setting_list_free(dev->site_specific[i].settings);
|
|
|
|
free(dev);
|
|
}
|
|
|
|
/* Used for debugging. */
|
|
void device_print(struct device *dev, int level)
|
|
{
|
|
int i;
|
|
|
|
printf("%*sdevice at %p:\n", level, "", (void *) dev);
|
|
if (!dev)
|
|
return;
|
|
printf("%*stype=%s id=%s devid=%p proc=%d\n", level + 4, "",
|
|
dev->subtype->name, dev->id, dev->devid, dev->processed);
|
|
|
|
printf("%*sactive:\n", level + 4, "");
|
|
printf("%*sexists=%d mod=%d deconf=%d def=%d blacklisted=%d\n",
|
|
level + 8, "", dev->active.exists, dev->active.modified,
|
|
dev->active.deconfigured, dev->active.definable,
|
|
dev->active.blacklisted);
|
|
if (dev->active.settings)
|
|
setting_list_print(dev->active.settings, level + 8);
|
|
else
|
|
printf("%*s<none>\n", level + 8, "");
|
|
|
|
printf("%*spersistent:\n", level + 4, "");
|
|
printf("%*sexists=%d mod=%d deconf=%d\n",
|
|
level + 8, "", dev->persistent.exists, dev->persistent.modified,
|
|
dev->persistent.deconfigured);
|
|
if (dev->persistent.settings)
|
|
setting_list_print(dev->persistent.settings, level + 8);
|
|
else
|
|
printf("%*s<none>\n", level + 8, "");
|
|
|
|
printf("%*sautoconf:\n", level + 4, "");
|
|
printf("%*sexists=%d mod=%d deconf=%d\n",
|
|
level + 8, "", dev->autoconf.exists, dev->autoconf.modified,
|
|
dev->autoconf.deconfigured);
|
|
if (dev->autoconf.settings)
|
|
setting_list_print(dev->autoconf.settings, level + 8);
|
|
else
|
|
printf("%*s<none>\n", level + 8, "");
|
|
|
|
printf("%*spersistent-site-specific:\n", level + 4, "");
|
|
for (i = 0; i < NUM_SITES; i++) {
|
|
printf("%*sexists=%d mod=%d deconf=%d\n",
|
|
level + 8, "", dev->site_specific[i].exists,
|
|
dev->site_specific[i].modified,
|
|
dev->site_specific[i].deconfigured);
|
|
if (dev->site_specific[i].settings)
|
|
setting_list_print(dev->site_specific[i].settings, level + 8);
|
|
else
|
|
printf("%*s<none>\n", level + 8, "");
|
|
}
|
|
}
|
|
|
|
char *device_get_sites(struct device *dev)
|
|
{
|
|
struct util_list *names;
|
|
char *str;
|
|
int i;
|
|
|
|
names = strlist_new();
|
|
for (i = 0; i < NUM_USER_SITES; i++) {
|
|
if (dev->site_specific[i].exists)
|
|
strlist_add(names, "%d", i);
|
|
}
|
|
|
|
str = strlist_flatten(names, ",");
|
|
strlist_free(names);
|
|
|
|
return str;
|
|
}
|
|
|
|
static const void *device_hash_get_id(void *dev_ptr)
|
|
{
|
|
struct device *dev = dev_ptr;
|
|
|
|
return dev->devid;
|
|
}
|
|
|
|
/* Create and initialize a new device_list. */
|
|
struct device_list *device_list_new(struct subtype *st)
|
|
{
|
|
struct device_list *list;
|
|
struct namespace *ns = st->namespace;
|
|
|
|
list = misc_malloc(sizeof(struct device_list));
|
|
hash_init(&list->hash, ns->hash_buckets, device_hash_get_id,
|
|
ns->cmp_parsed_ids, ns->hash_parsed_id,
|
|
struct device, node);
|
|
|
|
return list;
|
|
}
|
|
|
|
/* Release resources used by list and enlisted devices. */
|
|
void device_list_free(struct device_list *list)
|
|
{
|
|
if (!list)
|
|
return;
|
|
hash_clear(&list->hash, (void (*)(void *)) device_free);
|
|
free(list);
|
|
}
|
|
|
|
/* Add a new element to a list and mark the list as modified. */
|
|
void device_list_add(struct device_list *list, struct device *device)
|
|
{
|
|
hash_add(&list->hash, device);
|
|
list->modified = 1;
|
|
}
|
|
|
|
/* Find an element in the list. */
|
|
struct device *device_list_find(struct device_list *list, const char *id,
|
|
struct device *start)
|
|
{
|
|
struct device *dev;
|
|
struct namespace *ns;
|
|
void *devid;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
|
|
dev = start ? start : util_list_start(&list->hash.list);
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
ns = dev->subtype->namespace;
|
|
devid = ns->parse_id(id, err_ignore);
|
|
if (!devid)
|
|
goto out;
|
|
|
|
/* Try to find using hashed ID data. */
|
|
if (!start && list->hash.get_hash) {
|
|
dev = hash_find_by_id(&list->hash, devid);
|
|
goto out;
|
|
}
|
|
|
|
/* Find the slow way. */
|
|
while (dev) {
|
|
if (ns->cmp_parsed_ids(dev->devid, devid) == 0)
|
|
goto out;
|
|
dev = util_list_next(&list->hash.list, dev);
|
|
}
|
|
|
|
out:
|
|
free(devid);
|
|
|
|
return dev;
|
|
}
|
|
|
|
/* Used for debugging. */
|
|
void device_list_print(struct device_list *list, int level)
|
|
{
|
|
struct device *dev;
|
|
|
|
printf("%*sdevice list at %p:\n", level, "", (void *) list);
|
|
util_list_iterate(&list->hash.list, dev)
|
|
device_print(dev, level + 4);
|
|
}
|
|
|
|
/* Check if a device configuration needs to be written. */
|
|
bool device_needs_writing(struct device *dev, config_t config)
|
|
{
|
|
if (SCOPE_ACTIVE(config) &&
|
|
(dev->active.modified || dev->active.deconfigured ||
|
|
setting_list_modified(dev->active.settings)))
|
|
return true;
|
|
if (SCOPE_PERSISTENT(config) &&
|
|
(dev->persistent.modified || dev->persistent.deconfigured ||
|
|
setting_list_modified(dev->persistent.settings)))
|
|
return true;
|
|
if (SCOPE_AUTOCONF(config) &&
|
|
(dev->autoconf.modified || dev->autoconf.deconfigured ||
|
|
setting_list_modified(dev->autoconf.settings)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static exit_code_t apply_setting(struct device *dev, config_t config,
|
|
const char *key, const char *value,
|
|
struct util_list *processed)
|
|
{
|
|
struct subtype *st = dev->subtype;
|
|
struct attrib *a;
|
|
struct setting *s;
|
|
bool warn_readonly = false;
|
|
|
|
/* Check for known attribute. */
|
|
a = subtype_find_dev_attrib(st, key);
|
|
if (a) {
|
|
/* Check for read-only attribute. */
|
|
if (a->readonly)
|
|
goto err_readonly;
|
|
/* Check for acceptable value of known attribute. */
|
|
if (!force && !attrib_check_value(a, value))
|
|
goto err_invalid_forceable;
|
|
/* Check for activeonly. */
|
|
if (!force && SCOPE_PERSISTENT(config) && a->activeonly)
|
|
goto err_activeonly_forceable;
|
|
if (!force && SCOPE_AUTOCONF(config) && a->activeonly)
|
|
goto err_activeonly_forceable;
|
|
/* Check for internal. */
|
|
if (config == config_active && a->internal)
|
|
goto err_int_noactive;
|
|
/* Check for multiple values. */
|
|
if (!force && !a->multi && strlist_find(processed, key))
|
|
goto err_multi_forceable;
|
|
} else {
|
|
/* Handle unknown attribute. */
|
|
if (!st->unknown_dev_attribs)
|
|
goto err_unknown;
|
|
if (!force)
|
|
goto err_unknown_forceable;
|
|
/* Check for internal. */
|
|
if (config == config_active && internal_by_name(key))
|
|
goto err_int_noactive;
|
|
}
|
|
|
|
strlist_add(processed, "%s", key);
|
|
|
|
/* Apply to active config. */
|
|
if (SCOPE_ACTIVE(config)) {
|
|
s = setting_list_apply_specified(dev->active.settings, a,
|
|
key, value);
|
|
if (s->readonly)
|
|
warn_readonly = true;
|
|
}
|
|
|
|
/* Apply to persistent config. */
|
|
if (SCOPE_PERSISTENT(config)) {
|
|
setting_list_apply_specified(dev->persistent.settings,
|
|
a, key, value);
|
|
}
|
|
|
|
/* Apply to autoconf config. */
|
|
if (SCOPE_AUTOCONF(config)) {
|
|
setting_list_apply_specified(dev->autoconf.settings,
|
|
a, key, value);
|
|
}
|
|
|
|
/* Additional warning when trying to persist read-only setting. */
|
|
if (!SCOPE_ACTIVE(config) && (SCOPE_PERSISTENT(config) ||
|
|
SCOPE_AUTOCONF(config))) {
|
|
s = setting_list_find(dev->active.settings, key);
|
|
if (s && s->readonly)
|
|
warn_readonly = true;
|
|
}
|
|
|
|
if (warn_readonly)
|
|
delayed_warn("Modifying read-only attribute: %s\n", key);
|
|
|
|
return EXIT_OK;
|
|
|
|
err_invalid_forceable:
|
|
delayed_forceable("Invalid value for %s attribute: %s=%s\n",
|
|
dev->subtype->name, key, value);
|
|
delayed_info("Acceptable values:\n");
|
|
attrib_print_acceptable(a, -1);
|
|
delayed_info("Use '%s %s --help-attribute %s' for more "
|
|
"information\n", toolname, dev->subtype->name, key);
|
|
return EXIT_INVALID_SETTING;
|
|
|
|
err_multi_forceable:
|
|
delayed_forceable("Cannot specify multiple values for attribute '%s'\n",
|
|
key);
|
|
return EXIT_INVALID_SETTING;
|
|
|
|
err_unknown:
|
|
delayed_err("Unknown %s attribute specified: %s\n", st->devname, key);
|
|
return EXIT_ATTRIB_NOT_FOUND;
|
|
|
|
err_unknown_forceable:
|
|
delayed_forceable("Unknown %s attribute specified: %s\n",
|
|
st->devname, key);
|
|
return EXIT_ATTRIB_NOT_FOUND;
|
|
|
|
err_activeonly_forceable:
|
|
delayed_forceable("Attribute '%s' should only be changed in the active "
|
|
"config\n", a->name);
|
|
return EXIT_INVALID_SETTING;
|
|
|
|
err_int_noactive:
|
|
delayed_err("Internal attribute '%s' cannot be set in the active config\n",
|
|
key);
|
|
return EXIT_INVALID_SETTING;
|
|
|
|
err_readonly:
|
|
delayed_err("Cannot modify read-only attribute: %s\n", key);
|
|
return EXIT_INVALID_SETTING;
|
|
}
|
|
|
|
/* Apply device settings from strlist to device. */
|
|
exit_code_t device_apply_strlist(struct device *dev, config_t config,
|
|
struct util_list *settings)
|
|
{
|
|
struct util_list *processed;
|
|
struct strlist_node *s;
|
|
exit_code_t rc;
|
|
char *key, *value;
|
|
|
|
/* Apply settings. */
|
|
processed = strlist_new();
|
|
rc = EXIT_OK;
|
|
util_list_iterate(settings, s) {
|
|
key = misc_strdup(s->str);
|
|
value = strchr(key, '=');
|
|
*value = 0;
|
|
value++;
|
|
|
|
rc = apply_setting(dev, config, key, value, processed);
|
|
free(key);
|
|
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
strlist_free(processed);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* Apply device settings from setting_list to device. */
|
|
exit_code_t device_apply_settings(struct device *dev, config_t config,
|
|
struct util_list *settings)
|
|
{
|
|
struct util_list *processed;
|
|
struct setting *s;
|
|
exit_code_t rc;
|
|
|
|
/* Apply settings. */
|
|
processed = strlist_new();
|
|
rc = EXIT_OK;
|
|
util_list_iterate(settings, s) {
|
|
rc = apply_setting(dev, config, s->name, s->value, processed);
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
strlist_free(processed);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void reset_device_state(struct device_state *state)
|
|
{
|
|
setting_list_free(state->settings);
|
|
state->settings = setting_list_new();
|
|
state->exists = 0;
|
|
state->modified = 0;
|
|
state->deconfigured = 0;
|
|
state->definable = 0;
|
|
state->blacklisted = 0;
|
|
}
|
|
|
|
void device_reset(struct device *dev, config_t config)
|
|
{
|
|
if (SCOPE_ACTIVE(config))
|
|
reset_device_state(&dev->active);
|
|
if (SCOPE_PERSISTENT(config))
|
|
reset_device_state(&dev->persistent);
|
|
if (SCOPE_AUTOCONF(config))
|
|
reset_device_state(&dev->autoconf);
|
|
dev->processed = 0;
|
|
}
|
|
|
|
void device_add_modules(struct util_list *modules, struct device *dev)
|
|
{
|
|
struct subtype *st = dev->subtype;
|
|
|
|
/* Add dynamic module info. */
|
|
subtype_add_modules(st, dev, modules);
|
|
|
|
/* Add static subtype module info. */
|
|
subtype_add_static_modules(modules, st);
|
|
|
|
/* Add static devtype module info. */
|
|
devtype_add_modules(modules, st->devtype, 0);
|
|
}
|
|
|
|
char *device_read_active_attrib(struct device *dev, const char *name)
|
|
{
|
|
struct subtype *st = dev->subtype;
|
|
char *path, *value, *link;
|
|
|
|
/* Try direct approach. */
|
|
value = subtype_get_active_attrib(st, dev, name);
|
|
if (value)
|
|
return value;
|
|
|
|
/* Try reading from path. */
|
|
path = subtype_get_active_attrib_path(st, dev, name);
|
|
if (!path)
|
|
return NULL;
|
|
value = misc_read_text_file(path, 1, err_ignore);
|
|
if (!value) {
|
|
/* Symbolic links count as read-only attributes. */
|
|
link = misc_readlink(path);
|
|
if (link) {
|
|
value = misc_strdup(basename(link));
|
|
free(link);
|
|
}
|
|
}
|
|
free(path);
|
|
|
|
return value;
|
|
}
|
|
|
|
/* Return a newly allocated strlist of attribute names for @dev based on
|
|
* @scope. */
|
|
static struct util_list *get_attrib_names(struct device *dev,
|
|
read_scope_t scope)
|
|
{
|
|
struct subtype *st = dev->subtype;
|
|
struct util_list *names, *files;
|
|
struct strlist_node *s;
|
|
int i;
|
|
struct attrib *a;
|
|
const char *prefix;
|
|
char *path;
|
|
|
|
names = strlist_new();
|
|
|
|
/* Start with known and mandatory attributes. */
|
|
for (i = 0; (a = st->dev_attribs[i]); i++) {
|
|
if (scope != scope_mandatory || a->mandatory)
|
|
strlist_add(names, a->name);
|
|
}
|
|
if (scope != scope_all)
|
|
goto out;
|
|
|
|
/* Add attributes based on readable files in any of the prefix
|
|
* directories. */
|
|
prefix = "";
|
|
i = 0;
|
|
do {
|
|
path = subtype_get_active_attrib_path(st, dev, prefix);
|
|
if (!path)
|
|
continue;
|
|
|
|
/* Add attribute name for each file in path. */
|
|
files = strlist_new();
|
|
misc_read_dir(path, files, NULL, NULL);
|
|
util_list_iterate(files, s) {
|
|
if (*prefix) {
|
|
strlist_add_unique(names, "%s/%s", prefix,
|
|
s->str);
|
|
} else
|
|
strlist_add_unique(names, s->str);
|
|
}
|
|
strlist_free(files);
|
|
|
|
free(path);
|
|
} while (st->prefixes && (prefix = st->prefixes[i++]));
|
|
|
|
out:
|
|
return names;
|
|
}
|
|
|
|
/* Read settings according to @scope for device @dev from active
|
|
* configuration and add them to dev->active.settings. */
|
|
void device_read_active_settings(struct device *dev, read_scope_t scope)
|
|
{
|
|
struct subtype *st = dev->subtype;
|
|
struct util_list *names;
|
|
struct strlist_node *str;
|
|
char *path, *name, *value, *link;
|
|
struct attrib *a;
|
|
struct setting *s;
|
|
|
|
/* Expand scope. */
|
|
names = get_attrib_names(dev, scope);
|
|
|
|
util_list_iterate(names, str) {
|
|
name = str->str;
|
|
|
|
/* Don't add uevent attribute. */
|
|
if (strcmp(name, "uevent") == 0 || ends_with(name, "/uevent"))
|
|
continue;
|
|
|
|
/* Determine full attribute path. */
|
|
path = subtype_get_active_attrib_path(st, dev, name);
|
|
if (!path)
|
|
continue;
|
|
|
|
/* Get attribute value. */
|
|
link = NULL;
|
|
value = misc_read_text_file(path, 1, err_ignore);
|
|
if (!value) {
|
|
if (scope != scope_all)
|
|
goto next;
|
|
|
|
/* Register symbolic links as readonly attributes. */
|
|
link = misc_readlink(path);
|
|
if (!link)
|
|
goto next;
|
|
|
|
value = basename(link);
|
|
}
|
|
|
|
/* Apply setting to list. */
|
|
a = attrib_find(st->dev_attribs, name);
|
|
s = setting_list_apply_actual(dev->active.settings, a, name,
|
|
value);
|
|
if ((a && a->readonly) || link || (scope == scope_all &&
|
|
util_path_is_readonly_file("%s", path)))
|
|
s->readonly = 1;
|
|
if (link)
|
|
free(link);
|
|
else
|
|
free(value);
|
|
next:
|
|
free(path);
|
|
}
|
|
|
|
strlist_free(names);
|
|
}
|
|
|
|
/* Apply modified settings in @dev->active.settings to active configuration.
|
|
* Abort on first error. This requires that @dev defines
|
|
* get_active_attrib_path. */
|
|
exit_code_t device_write_active_settings(struct device *dev)
|
|
{
|
|
struct subtype *st = dev->subtype;
|
|
struct util_list *list;
|
|
struct ptrlist_node *p;
|
|
struct setting *s;
|
|
char *path;
|
|
exit_code_t rc = EXIT_OK;
|
|
|
|
/* Get order of applying attributes. */
|
|
list = setting_list_get_sorted(dev->active.settings);
|
|
|
|
/* Apply settings in order. */
|
|
util_list_iterate(list, p) {
|
|
s = p->ptr;
|
|
if (!s->modified || s->removed)
|
|
continue;
|
|
if ((s->attrib && s->attrib->internal) ||
|
|
internal_by_name(s->name))
|
|
continue;
|
|
|
|
path = subtype_get_active_attrib_path(st, dev, s->name);
|
|
if (!path) {
|
|
delayed_err("Could not determine path for attribute "
|
|
"'%s'\n", s->name);
|
|
rc = EXIT_SETTING_NOT_FOUND;
|
|
break;
|
|
}
|
|
rc = setting_write(path, s);
|
|
free(path);
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
ptrlist_free(list, 0);
|
|
|
|
/* Changing device configuration could generate uevents. */
|
|
udev_need_settle = 1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* Check if there are any conflicts in the settings list of @dev for the
|
|
* specified configuration. */
|
|
exit_code_t device_check_settings(struct device *dev, config_t config,
|
|
err_t err)
|
|
{
|
|
struct setting *s;
|
|
|
|
if (SCOPE_ACTIVE(config)) {
|
|
/* Ensure that actual values are available or we might
|
|
* report false positives. */
|
|
util_list_iterate(&dev->active.settings->list, s) {
|
|
if (s->actual_value || s->removed ||
|
|
(!s->modified && !s->specified))
|
|
continue;
|
|
s->actual_value =
|
|
device_read_active_attrib(dev, s->name);
|
|
}
|
|
|
|
if (!setting_list_check_conflict(dev->active.settings,
|
|
config_active,
|
|
err))
|
|
return EXIT_INVALID_CONFIG;
|
|
}
|
|
if (SCOPE_PERSISTENT(config)) {
|
|
if (!setting_list_check_conflict(dev->persistent.settings,
|
|
config_persistent,
|
|
err))
|
|
return EXIT_INVALID_CONFIG;
|
|
}
|
|
if (SCOPE_AUTOCONF(config)) {
|
|
if (!setting_list_check_conflict(dev->autoconf.settings,
|
|
config_autoconf,
|
|
err))
|
|
return EXIT_INVALID_CONFIG;
|
|
}
|
|
|
|
return EXIT_OK;
|
|
}
|
|
|
|
struct setting_list *device_get_setting_list(struct device *dev,
|
|
config_t config,
|
|
int site_id)
|
|
{
|
|
struct setting_list *settings = NULL;
|
|
|
|
if (site_id < SITE_FALLBACK) {
|
|
settings = dev->site_specific[site_id].settings;
|
|
goto out;
|
|
}
|
|
|
|
if (config == config_active)
|
|
settings = dev->active.settings;
|
|
else if (config == config_persistent)
|
|
settings = dev->persistent.settings;
|
|
else if (config == config_autoconf)
|
|
settings = dev->autoconf.settings;
|
|
|
|
out:
|
|
return settings;
|
|
}
|
|
|
|
/* Return configuration set in which device exists. */
|
|
config_t device_get_config(struct device *dev)
|
|
{
|
|
config_t config = 0;
|
|
|
|
if (dev->active.exists || dev->active.definable)
|
|
config |= config_active;
|
|
|
|
if (dev->persistent.exists)
|
|
config |= config_persistent;
|
|
|
|
if (dev->autoconf.exists)
|
|
config |= config_autoconf;
|
|
|
|
return config;
|
|
}
|
|
|
|
/* Determine if persistent configuration available for any of the sites*/
|
|
bool is_dev_pers(struct device *dev)
|
|
{
|
|
int i;
|
|
|
|
if (global_site_id != SITE_FALLBACK &&
|
|
!dev->site_specific[global_site_id].exists)
|
|
return false;
|
|
else if (dev->persistent.exists)
|
|
return true;
|
|
|
|
for (i = 0; i < NUM_SITES; i++)
|
|
if (dev->site_specific[i].exists)
|
|
return true;
|
|
|
|
return false;
|
|
}
|