blob: 07925680c1f67f81e886c1426500514777f79f78 [file] [log] [blame]
/**
* Copyright (C) ARM Limited 2013. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "Hwmon.h"
#include "libsensors/sensors.h"
#include "Buffer.h"
#include "Counter.h"
#include "Logging.h"
#include "SessionData.h"
class HwmonCounter {
public:
HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature);
~HwmonCounter();
HwmonCounter *getNext() const { return next; }
int getKey() const { return key; }
bool isEnabled() const { return enabled; }
const char *getName() const { return name; }
const char *getLabel() const { return label; }
const char *getTitle() const { return title; }
bool isDuplicate() const { return duplicate; }
const char *getDisplay() const { return display; }
const char *getUnit() const { return unit; }
int getModifier() const { return modifier; }
void setEnabled(const bool enabled) {
this->enabled = enabled;
// canRead will clear enabled if the counter is not readable
canRead();
}
double read();
bool canRead();
private:
void init(const sensors_chip_name *chip, const sensors_feature *feature);
HwmonCounter *const next;
const int key;
int polled : 1,
readable : 1,
enabled : 1,
monotonic: 1,
duplicate : 1;
const sensors_chip_name *chip;
const sensors_feature *feature;
char *name;
char *label;
const char *title;
const char *display;
const char *unit;
int modifier;
double previous_value;
sensors_subfeature_type input;
};
HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) {
int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1;
char *chip_name = new char[len];
sensors_snprintf_chip_name(chip_name, len, chip);
len = snprintf(NULL, 0, "hwmon_%s_%d", chip_name, feature->number) + 1;
name = new char[len];
snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number);
delete [] chip_name;
label = sensors_get_label(chip, feature);
switch (feature->type) {
case SENSORS_FEATURE_IN:
title = "Voltage";
input = SENSORS_SUBFEATURE_IN_INPUT;
display = "average";
unit = "V";
modifier = 1000;
monotonic = false;
break;
case SENSORS_FEATURE_FAN:
title = "Fan";
input = SENSORS_SUBFEATURE_FAN_INPUT;
display = "average";
unit = "RPM";
modifier = 1;
monotonic = false;
break;
case SENSORS_FEATURE_TEMP:
title = "Temperature";
input = SENSORS_SUBFEATURE_TEMP_INPUT;
display = "maximum";
unit = "°C";
modifier = 1000;
monotonic = false;
break;
case SENSORS_FEATURE_POWER:
title = "Power";
input = SENSORS_SUBFEATURE_POWER_INPUT;
display = "average";
unit = "W";
modifier = 1000000;
monotonic = false;
break;
case SENSORS_FEATURE_ENERGY:
title = "Energy";
input = SENSORS_SUBFEATURE_ENERGY_INPUT;
display = "accumulate";
unit = "J";
modifier = 1000000;
monotonic = true;
break;
case SENSORS_FEATURE_CURR:
title = "Current";
input = SENSORS_SUBFEATURE_CURR_INPUT;
display = "average";
unit = "A";
modifier = 1000;
monotonic = false;
break;
case SENSORS_FEATURE_HUMIDITY:
title = "Humidity";
input = SENSORS_SUBFEATURE_HUMIDITY_INPUT;
display = "average";
unit = "%";
modifier = 1000;
monotonic = false;
break;
default:
logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type);
handleException();
}
for (HwmonCounter * counter = next; counter != NULL; counter = counter->getNext()) {
if (strcmp(label, counter->getLabel()) == 0 && strcmp(title, counter->getTitle()) == 0) {
duplicate = true;
counter->duplicate = true;
break;
}
}
}
HwmonCounter::~HwmonCounter() {
free((void *)label);
delete [] name;
}
double HwmonCounter::read() {
double value;
double result;
const sensors_subfeature *subfeature;
// Keep in sync with canRead
subfeature = sensors_get_subfeature(chip, feature, input);
if (!subfeature) {
logg->logError(__FILE__, __LINE__, "No input value for hwmon sensor %s", label);
handleException();
}
if (sensors_get_value(chip, subfeature->number, &value) != 0) {
logg->logError(__FILE__, __LINE__, "Can't get input value for hwmon sensor %s", label);
handleException();
}
result = (monotonic ? value - previous_value : value);
previous_value = value;
return result;
}
bool HwmonCounter::canRead() {
if (!polled) {
double value;
const sensors_subfeature *subfeature;
bool result = true;
subfeature = sensors_get_subfeature(chip, feature, input);
if (!subfeature) {
result = false;
} else {
result = sensors_get_value(chip, subfeature->number, &value) == 0;
}
polled = true;
readable = result;
}
enabled &= readable;
return readable;
}
Hwmon::Hwmon() : counters(NULL) {
int err = sensors_init(NULL);
if (err) {
logg->logMessage("Failed to initialize libsensors! (%d)", err);
return;
}
sensors_sysfs_no_scaling = 1;
int chip_nr = 0;
const sensors_chip_name *chip;
while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) {
int feature_nr = 0;
const sensors_feature *feature;
while ((feature = sensors_get_features(chip, &feature_nr))) {
counters = new HwmonCounter(counters, getEventKey(), chip, feature);
}
}
}
Hwmon::~Hwmon() {
while (counters != NULL) {
HwmonCounter * counter = counters;
counters = counter->getNext();
delete counter;
}
sensors_cleanup();
}
HwmonCounter *Hwmon::findCounter(const Counter &counter) const {
for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) {
if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) {
return hwmonCounter;
}
}
return NULL;
}
bool Hwmon::claimCounter(const Counter &counter) const {
return findCounter(counter) != NULL;
}
bool Hwmon::countersEnabled() const {
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
if (counter->isEnabled()) {
return true;
}
}
return false;
}
void Hwmon::resetCounters() {
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
counter->setEnabled(false);
}
}
void Hwmon::setupCounter(Counter &counter) {
HwmonCounter *const hwmonCounter = findCounter(counter);
if (hwmonCounter == NULL) {
counter.setEnabled(false);
return;
}
hwmonCounter->setEnabled(true);
counter.setKey(hwmonCounter->getKey());
}
void Hwmon::writeCounters(mxml_node_t *root) const {
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
if (!counter->canRead()) {
continue;
}
mxml_node_t *node = mxmlNewElement(root, "counter");
mxmlElementSetAttr(node, "name", counter->getName());
}
}
void Hwmon::writeEvents(mxml_node_t *root) const {
root = mxmlNewElement(root, "category");
mxmlElementSetAttr(root, "name", "hwmon");
char buf[1024];
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
if (!counter->canRead()) {
continue;
}
mxml_node_t *node = mxmlNewElement(root, "event");
mxmlElementSetAttr(node, "counter", counter->getName());
mxmlElementSetAttr(node, "title", counter->getTitle());
if (counter->isDuplicate()) {
mxmlElementSetAttrf(node, "name", "%s (0x%x)", counter->getLabel(), counter->getKey());
} else {
mxmlElementSetAttr(node, "name", counter->getLabel());
}
mxmlElementSetAttr(node, "display", counter->getDisplay());
mxmlElementSetAttr(node, "units", counter->getUnit());
if (counter->getModifier() != 1) {
mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier());
}
if (strcmp(counter->getDisplay(), "average") == 0 || strcmp(counter->getDisplay(), "maximum") == 0) {
mxmlElementSetAttr(node, "average_selection", "yes");
}
snprintf(buf, sizeof(buf), "libsensors %s sensor %s (%s)", counter->getTitle(), counter->getLabel(), counter->getName());
mxmlElementSetAttr(node, "description", buf);
}
}
void Hwmon::start() {
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
if (!counter->isEnabled()) {
continue;
}
counter->read();
}
}
void Hwmon::read(Buffer * const buffer) {
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
if (!counter->isEnabled()) {
continue;
}
buffer->event(counter->getKey(), counter->read());
}
}