Files
lm-sensors/lib/sysfs.c
T
Jean Delvare ed480f2edd Optimize the memory consumption in sensors_read_dynamic_chip(). The
idea is to have separate indexing for subfeatures with a compute
mapping and for subfeatures without it. They still follow each
other in the big table but this avoids wasting memory due to the
numbering gap between them.

This cuts the amount of memory (temporarily) allocated by
sensors_read_dynamic_chip() almost by half, and also speeds it up
a little as it now takes less iterations to walk the sparse array.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/branches/lm-sensors-3.0.0@4761 7894878c-1315-0410-8ee3-d5d059ff63e0
2007-09-05 08:20:35 +00:00

484 lines
12 KiB
C

/*
sysfs.c - Part of libsensors, a library for reading Linux sensor data
Copyright (c) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this define needed for strndup() */
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <sysfs/libsysfs.h>
#include "data.h"
#include "error.h"
#include "access.h"
#include "general.h"
#include "sysfs.h"
char sensors_sysfs_mount[NAME_MAX];
#define MAX_SENSORS_PER_TYPE 16
#define MAX_SUB_FEATURES 6
/* Room for all 3 types (in, fan, temp) with all their subfeatures + VID */
#define ALL_POSSIBLE_FEATURES (MAX_SENSORS_PER_TYPE * MAX_SUB_FEATURES * 6 \
+ MAX_SENSORS_PER_TYPE)
static
int get_type_scaling(int type)
{
switch (type & 0xFF10) {
case SENSORS_FEATURE_IN:
case SENSORS_FEATURE_TEMP:
return 3;
case SENSORS_FEATURE_FAN:
return 0;
}
switch (type) {
case SENSORS_FEATURE_VID:
return 3;
default:
return 0;
}
}
static int sensors_read_dynamic_chip(sensors_chip_features *chip,
struct sysfs_device *sysdir)
{
int i, type, fnum = 0;
struct sysfs_attribute *attr;
struct dlist *attrs;
sensors_chip_feature *features;
sensors_chip_feature *dyn_features;
char *name;
attrs = sysfs_get_device_attributes(sysdir);
if (attrs == NULL)
return -ENOENT;
/* We use a large sparse table at first to store all found features,
so that we can store them sorted at type and index and then later
create a dense sorted table. */
features = calloc(ALL_POSSIBLE_FEATURES, sizeof(sensors_chip_feature));
if (!features)
sensors_fatal_error(__FUNCTION__, "Out of memory");
dlist_for_each_data(attrs, attr, struct sysfs_attribute) {
sensors_chip_feature feature;
name = attr->name;
int nr;
type = sensors_feature_get_type(name, &nr);
if (type == SENSORS_FEATURE_UNKNOWN)
continue;
memset(&feature, 0, sizeof(sensors_chip_feature));
/* check for _input extension and remove */
i = strlen(name);
if (i > 6 && !strcmp(name + i - 6, "_input"))
feature.data.name = strndup(name, i-6);
else
feature.data.name = strdup(name);
/* Adjust the channel number */
switch (type & 0xFF00) {
case SENSORS_FEATURE_FAN:
case SENSORS_FEATURE_TEMP:
if (nr)
nr--;
break;
}
if (nr >= MAX_SENSORS_PER_TYPE) {
fprintf(stderr, "libsensors error, more sensors of one"
" type then MAX_SENSORS_PER_TYPE, ignoring "
"feature: %s\n", name);
free(feature.data.name);
continue;
}
/* "calculate" a place to store the feature in our sparse,
sorted table */
if (type == SENSORS_FEATURE_VID) {
i = nr + MAX_SENSORS_PER_TYPE * MAX_SUB_FEATURES * 6;
} else {
i = (type >> 8) * MAX_SENSORS_PER_TYPE *
MAX_SUB_FEATURES * 2 + nr * MAX_SUB_FEATURES * 2 +
((type & 0x10) >> 4) * MAX_SUB_FEATURES +
(type & 0x0F);
}
if (features[i].data.name) {
fprintf(stderr, "libsensors error, trying to add dupli"
"cate feature: %s to dynamic feature table\n",
name);
free(feature.data.name);
continue;
}
/* fill in the other feature members */
feature.data.number = i;
feature.data.type = type;
if ((type & 0x00FF) == 0) {
/* main feature */
feature.data.mapping = SENSORS_NO_MAPPING;
} else {
/* sub feature */
feature.data.mapping = i - i % (MAX_SUB_FEATURES * 2);
if (!(type & 0x10))
feature.data.flags |= SENSORS_COMPUTE_MAPPING;
}
if (attr->method & SYSFS_METHOD_SHOW)
feature.data.flags |= SENSORS_MODE_R;
if (attr->method & SYSFS_METHOD_STORE)
feature.data.flags |= SENSORS_MODE_W;
feature.scaling = get_type_scaling(type);
features[i] = feature;
fnum++;
}
if (!fnum) { /* No feature */
chip->feature = NULL;
goto exit_free;
}
dyn_features = calloc(fnum, sizeof(sensors_chip_feature));
if (dyn_features == NULL) {
sensors_fatal_error(__FUNCTION__, "Out of memory");
}
fnum = 0;
for (i = 0; i < ALL_POSSIBLE_FEATURES; i++) {
if (features[i].data.name) {
dyn_features[fnum] = features[i];
fnum++;
}
}
/* Renumber the features linearly, so that feature number N is at
position N in the array. This allows for O(1) look-ups. */
for (i = 0; i < fnum; i++) {
int j, old;
old = dyn_features[i].data.number;
dyn_features[i].data.number = i;
for (j = i + 1;
j < fnum && dyn_features[j].data.mapping != SENSORS_NO_MAPPING;
j++) {
if (dyn_features[j].data.mapping == old)
dyn_features[j].data.mapping = i;
}
}
chip->feature = dyn_features;
chip->feature_count = fnum;
exit_free:
free(features);
return 0;
}
/* returns !0 if sysfs filesystem was found, 0 otherwise */
int sensors_init_sysfs(void)
{
struct stat statbuf;
/* libsysfs will return success even if sysfs is not mounted,
so we have to double-check */
if (sysfs_get_mnt_path(sensors_sysfs_mount, NAME_MAX)
|| stat(sensors_sysfs_mount, &statbuf) < 0
|| statbuf.st_nlink <= 2) /* Empty directory */
return 0;
return 1;
}
/* returns: 0 if successful, !0 otherwise */
static int sensors_read_one_sysfs_chip(struct sysfs_device *dev)
{
int domain, bus, slot, fn;
int err = -SENSORS_ERR_PARSE;
struct sysfs_attribute *attr, *bus_attr;
char bus_path[SYSFS_PATH_MAX];
sensors_chip_features entry;
/* ignore any device without name attribute */
if (!(attr = sysfs_get_device_attr(dev, "name")))
return 0;
/* NB: attr->value[attr->len-1] == '\n'; chop that off */
entry.chip.prefix = strndup(attr->value, attr->len - 1);
if (!entry.chip.prefix)
sensors_fatal_error(__FUNCTION__, "out of memory");
entry.chip.path = strdup(dev->path);
if (!entry.chip.path)
sensors_fatal_error(__FUNCTION__, "out of memory");
if (sscanf(dev->name, "%hd-%x", &entry.chip.bus.nr, &entry.chip.addr) == 2) {
/* find out if legacy ISA or not */
if (entry.chip.bus.nr == 9191) {
entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
entry.chip.bus.nr = 0;
} else {
entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
snprintf(bus_path, sizeof(bus_path),
"%s/class/i2c-adapter/i2c-%d/device/name",
sensors_sysfs_mount, entry.chip.bus.nr);
if ((bus_attr = sysfs_open_attribute(bus_path))) {
if (sysfs_read_attribute(bus_attr)) {
sysfs_close_attribute(bus_attr);
goto exit_free;
}
if (bus_attr->value
&& !strncmp(bus_attr->value, "ISA ", 4)) {
entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
entry.chip.bus.nr = 0;
}
sysfs_close_attribute(bus_attr);
}
}
} else if (sscanf(dev->name, "spi%hd.%d", &entry.chip.bus.nr,
&entry.chip.addr) == 2) {
/* SPI */
entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
} else if (sscanf(dev->name, "%*[a-z0-9_].%d", &entry.chip.addr) == 1) {
/* must be new ISA (platform driver) */
entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
entry.chip.bus.nr = 0;
} else if (sscanf(dev->name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
/* PCI */
entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
entry.chip.bus.nr = 0;
} else
goto exit_free;
if (sensors_read_dynamic_chip(&entry, dev) < 0)
goto exit_free;
if (!entry.feature) { /* No feature, discard chip */
err = 0;
goto exit_free;
}
sensors_add_proc_chips(&entry);
return 0;
exit_free:
free(entry.chip.prefix);
free(entry.chip.path);
return err;
}
/* returns 0 if successful, !0 otherwise */
static int sensors_read_sysfs_chips_compat(void)
{
struct sysfs_bus *bus;
struct dlist *devs;
struct sysfs_device *dev;
int ret = 0;
if (!(bus = sysfs_open_bus("i2c"))) {
if (errno && errno != ENOENT)
ret = -SENSORS_ERR_PROC;
goto exit0;
}
if (!(devs = sysfs_get_bus_devices(bus))) {
if (errno && errno != ENOENT)
ret = -SENSORS_ERR_PROC;
goto exit1;
}
dlist_for_each_data(devs, dev, struct sysfs_device)
if ((ret = sensors_read_one_sysfs_chip(dev)))
goto exit1;
exit1:
/* this frees bus and devs */
sysfs_close_bus(bus);
exit0:
return ret;
}
/* returns 0 if successful, !0 otherwise */
int sensors_read_sysfs_chips(void)
{
struct sysfs_class *cls;
struct dlist *clsdevs;
struct sysfs_class_device *clsdev;
int ret = 0;
if (!(cls = sysfs_open_class("hwmon"))) {
/* compatibility function for kernel 2.6.n where n <= 13 */
return sensors_read_sysfs_chips_compat();
}
if (!(clsdevs = sysfs_get_class_devices(cls))) {
if (errno && errno != ENOENT)
ret = -SENSORS_ERR_PROC;
goto exit;
}
dlist_for_each_data(clsdevs, clsdev, struct sysfs_class_device) {
struct sysfs_device *dev;
if (!(dev = sysfs_get_classdev_device(clsdev))) {
ret = -SENSORS_ERR_PROC;
goto exit;
}
if ((ret = sensors_read_one_sysfs_chip(dev)))
goto exit;
}
exit:
/* this frees cls and clsdevs */
sysfs_close_class(cls);
return ret;
}
/* returns 0 if successful, !0 otherwise */
int sensors_read_sysfs_bus(void)
{
struct sysfs_class *cls;
struct dlist *clsdevs;
struct sysfs_class_device *clsdev;
sensors_bus entry;
int ret = 0;
if (!(cls = sysfs_open_class("i2c-adapter"))) {
if (errno && errno != ENOENT)
ret = -SENSORS_ERR_PROC;
goto exit0;
}
if (!(clsdevs = sysfs_get_class_devices(cls))) {
if (errno && errno != ENOENT)
ret = -SENSORS_ERR_PROC;
goto exit1;
}
dlist_for_each_data(clsdevs, clsdev, struct sysfs_class_device) {
struct sysfs_device *dev;
struct sysfs_attribute *attr;
/* Get the adapter name from the classdev "name" attribute
* (Linux 2.6.20 and later). If it fails, fall back to
* the device "name" attribute (for older kernels). */
if (!(attr = sysfs_get_classdev_attr(clsdev, "name"))
&& !((dev = sysfs_get_classdev_device(clsdev)) &&
(attr = sysfs_get_device_attr(dev, "name"))))
continue;
if (sscanf(clsdev->name, "i2c-%hd", &entry.bus.nr) != 1 ||
entry.bus.nr == 9191) /* legacy ISA */
continue;
entry.bus.type = SENSORS_BUS_TYPE_I2C;
/* NB: attr->value[attr->len-1] == '\n'; chop that off */
entry.adapter = strndup(attr->value, attr->len - 1);
if (!entry.adapter)
sensors_fatal_error(__FUNCTION__, "out of memory");
sensors_add_proc_bus(&entry);
}
exit1:
/* this frees *cls _and_ *clsdevs */
sysfs_close_class(cls);
exit0:
return ret;
}
int sensors_read_sysfs_attr(const sensors_chip_name *name, int feature,
double *value)
{
const sensors_chip_feature *the_feature;
int mag;
char n[NAME_MAX];
FILE *f;
const char *suffix = "";
if (!(the_feature = sensors_lookup_feature_nr(name, feature)))
return -SENSORS_ERR_NO_ENTRY;
/* REVISIT: this is a ugly hack */
if (the_feature->data.type == SENSORS_FEATURE_IN
|| the_feature->data.type == SENSORS_FEATURE_FAN
|| the_feature->data.type == SENSORS_FEATURE_TEMP)
suffix = "_input";
snprintf(n, NAME_MAX, "%s/%s%s", name->path, the_feature->data.name,
suffix);
if ((f = fopen(n, "r"))) {
int res = fscanf(f, "%lf", value);
fclose(f);
if (res != 1)
return -SENSORS_ERR_PROC;
for (mag = the_feature->scaling; mag > 0; mag --)
*value /= 10.0;
} else
return -SENSORS_ERR_PROC;
return 0;
}
int sensors_write_sysfs_attr(const sensors_chip_name *name, int feature,
double value)
{
const sensors_chip_feature *the_feature;
int mag;
char n[NAME_MAX];
FILE *f;
const char *suffix = "";
if (!(the_feature = sensors_lookup_feature_nr(name, feature)))
return -SENSORS_ERR_NO_ENTRY;
/* REVISIT: this is a ugly hack */
if (the_feature->data.type == SENSORS_FEATURE_IN
|| the_feature->data.type == SENSORS_FEATURE_FAN
|| the_feature->data.type == SENSORS_FEATURE_TEMP)
suffix = "_input";
snprintf(n, NAME_MAX, "%s/%s%s", name->path, the_feature->data.name,
suffix);
if ((f = fopen(n, "w"))) {
for (mag = the_feature->scaling; mag > 0; mag --)
value *= 10.0;
fprintf(f, "%d", (int) value);
fclose(f);
} else
return -SENSORS_ERR_PROC;
return 0;
}