[PATCH 1/2] hwmon: add hwmon-core interface
Lucas Stach
dev at lynxeye.de
Fri Jul 1 00:55:30 CEST 2011
This adds an interface to easily access attributes from hwmon drivers and
a way to generically build sysfs entries for supported attributes.
---
drivers/hwmon/hwmon-core.c | 656 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/hwmon-core.h | 211 ++++++++++++++
2 files changed, 867 insertions(+), 0 deletions(-)
create mode 100644 drivers/hwmon/hwmon-core.c
create mode 100644 include/linux/hwmon-core.h
diff --git a/drivers/hwmon/hwmon-core.c b/drivers/hwmon/hwmon-core.c
new file mode 100644
index 0000000..eb39a45
--- /dev/null
+++ b/drivers/hwmon/hwmon-core.c
@@ -0,0 +1,656 @@
+/*
+ * hwmon-core.c
+ * Copyright (C) 2011 Lucas Stach
+ *
+ * hwmon-core interface implementation
+ *
+ * Provides functions to create/destroy a sysfs interface out of a
+ * hwmon_device_instance.
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/hwmon-core.h>
+#include <linux/hwmon-sysfs.h>
+
+#define HWMON_NAME_SIZE 32
+
+struct hwmon_device_attribute {
+ struct sensor_device_attribute sensor_dev;
+ struct hwmon_device_instance *hw_dev_inst;
+ char name[HWMON_NAME_SIZE];
+ struct list_head node;
+};
+
+#define to_hwmon_device_attr(_sensor_attr) \
+ container_of(_sensor_attr, struct hwmon_device_attribute, sensor_dev)
+
+#define HWMON_GET_TEXT_ACCESSOR(_name, _enum) \
+ static ssize_t _name(struct device *dev, \
+ struct device_attribute *devattr, char *buf) \
+ { \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
+ struct hwmon_device_attribute *hw_attr = to_hwmon_device_attr(attr); \
+ struct hwmon_device_instance *hw_dev = hw_attr->hw_dev_inst; \
+ return hw_dev->get_text_attr(hw_dev->inst_data, \
+ _enum, attr->index, buf); \
+ }
+
+#define HWMON_GET_NUM_ACCESSOR(_name, _enum) \
+ static ssize_t _name(struct device *dev, \
+ struct device_attribute *devattr, char *buf) \
+ { \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
+ struct hwmon_device_attribute *hw_attr = to_hwmon_device_attr(attr); \
+ struct hwmon_device_instance *hw_dev = hw_attr->hw_dev_inst; \
+ int value, ret; \
+ ret = hw_dev->get_numeric_attr(hw_dev->inst_data, \
+ _enum, attr->index, &value); \
+ if(ret) \
+ return ret; \
+ ret = snprintf(buf, PAGE_SIZE, "%u\n", value); \
+ return ret; \
+ }
+
+#define HWMON_SET_NUM_ACCESSOR(_name, _enum) \
+ static ssize_t _name(struct device *dev, \
+ struct device_attribute *devattr, const char *buf, size_t count) \
+ { \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
+ struct hwmon_device_attribute *hw_attr = to_hwmon_device_attr(attr); \
+ struct hwmon_device_instance *hw_dev = hw_attr->hw_dev_inst; \
+ long value; \
+ if(strict_strtol(buf, 10, &value)) \
+ return -EINVAL; \
+ hw_dev->set_numeric_attr(hw_dev->inst_data, \
+ _enum, attr->index, value); \
+ return count; \
+ }
+
+/* accessor functions to route sysfs to hwmon core api */
+
+HWMON_GET_TEXT_ACCESSOR(show_name, hwmon_attr_name)
+HWMON_GET_NUM_ACCESSOR(show_update_interval, hwmon_attr_update_interval)
+
+HWMON_GET_NUM_ACCESSOR(show_in_min, hwmon_attr_volt_min)
+HWMON_SET_NUM_ACCESSOR(set_in_min, hwmon_attr_volt_min)
+HWMON_GET_NUM_ACCESSOR(show_in_lcrit, hwmon_attr_volt_lcrit)
+HWMON_SET_NUM_ACCESSOR(set_in_lcrit, hwmon_attr_volt_lcrit)
+HWMON_GET_NUM_ACCESSOR(show_in_max, hwmon_attr_volt_max)
+HWMON_SET_NUM_ACCESSOR(set_in_max, hwmon_attr_volt_max)
+HWMON_GET_NUM_ACCESSOR(show_in_crit, hwmon_attr_volt_crit)
+HWMON_SET_NUM_ACCESSOR(set_in_crit, hwmon_attr_volt_crit)
+HWMON_GET_NUM_ACCESSOR(show_in_vid, hwmon_attr_volt_vid)
+HWMON_GET_TEXT_ACCESSOR(show_in_label, hwmon_attr_volt_label)
+HWMON_GET_NUM_ACCESSOR(show_in_input, hwmon_attr_volt_input)
+HWMON_GET_NUM_ACCESSOR(show_in_vrm, hwmon_attr_volt_vrm)
+
+HWMON_GET_NUM_ACCESSOR(show_fan_min, hwmon_attr_fan_min)
+HWMON_SET_NUM_ACCESSOR(set_fan_min, hwmon_attr_fan_min)
+HWMON_GET_NUM_ACCESSOR(show_fan_max, hwmon_attr_fan_max)
+HWMON_SET_NUM_ACCESSOR(set_fan_max, hwmon_attr_fan_max)
+HWMON_GET_NUM_ACCESSOR(show_fan_div, hwmon_attr_fan_div)
+HWMON_SET_NUM_ACCESSOR(set_fan_div, hwmon_attr_fan_div)
+HWMON_GET_NUM_ACCESSOR(show_fan_pulses, hwmon_attr_fan_pulses)
+HWMON_SET_NUM_ACCESSOR(set_fan_pulses, hwmon_attr_fan_pulses)
+HWMON_GET_NUM_ACCESSOR(show_fan_target, hwmon_attr_fan_target)
+HWMON_SET_NUM_ACCESSOR(set_fan_target, hwmon_attr_fan_target)
+HWMON_GET_TEXT_ACCESSOR(show_fan_label, hwmon_attr_fan_label)
+HWMON_GET_NUM_ACCESSOR(show_fan_input, hwmon_attr_fan_input)
+
+HWMON_GET_NUM_ACCESSOR(show_pwm, hwmon_attr_pwm)
+HWMON_SET_NUM_ACCESSOR(set_pwm, hwmon_attr_pwm)
+HWMON_GET_NUM_ACCESSOR(show_pwm_enable, hwmon_attr_pwm_enable)
+HWMON_SET_NUM_ACCESSOR(set_pwm_enable, hwmon_attr_pwm_enable)
+HWMON_GET_NUM_ACCESSOR(show_pwm_mode, hwmon_attr_pwm_mode)
+HWMON_SET_NUM_ACCESSOR(set_pwm_mode, hwmon_attr_pwm_mode)
+HWMON_GET_NUM_ACCESSOR(show_pwm_freq, hwmon_attr_pwm_freq)
+HWMON_SET_NUM_ACCESSOR(set_pwm_freq, hwmon_attr_pwm_freq)
+HWMON_GET_NUM_ACCESSOR(show_pwm_auto_ct, hwmon_attr_pwm_auto_channels_temp)
+HWMON_SET_NUM_ACCESSOR(set_pwm_auto_ct, hwmon_attr_pwm_auto_channels_temp)
+
+HWMON_GET_NUM_ACCESSOR(show_temp_input, hwmon_attr_temp_input)
+HWMON_GET_NUM_ACCESSOR(show_temp_type, hwmon_attr_temp_type)
+HWMON_SET_NUM_ACCESSOR(set_temp_type, hwmon_attr_temp_type)
+HWMON_GET_NUM_ACCESSOR(show_temp_max, hwmon_attr_temp_max)
+HWMON_SET_NUM_ACCESSOR(set_temp_max, hwmon_attr_temp_max)
+HWMON_GET_NUM_ACCESSOR(show_temp_min, hwmon_attr_temp_min)
+HWMON_SET_NUM_ACCESSOR(set_temp_min, hwmon_attr_temp_min)
+HWMON_GET_NUM_ACCESSOR(show_temp_max_hyst, hwmon_attr_temp_max_hyst)
+HWMON_SET_NUM_ACCESSOR(set_temp_max_hyst, hwmon_attr_temp_max_hyst)
+HWMON_GET_NUM_ACCESSOR(show_temp_crit, hwmon_attr_temp_crit)
+HWMON_SET_NUM_ACCESSOR(set_temp_crit, hwmon_attr_temp_crit)
+HWMON_GET_NUM_ACCESSOR(show_temp_crit_hyst, hwmon_attr_temp_crit_hyst)
+HWMON_SET_NUM_ACCESSOR(set_temp_crit_hyst, hwmon_attr_temp_crit_hyst)
+HWMON_GET_NUM_ACCESSOR(show_temp_emergency, hwmon_attr_temp_emergency)
+HWMON_SET_NUM_ACCESSOR(set_temp_emergency, hwmon_attr_temp_emergency)
+HWMON_GET_NUM_ACCESSOR(show_temp_emergency_hyst, hwmon_attr_temp_emergency_hyst)
+HWMON_SET_NUM_ACCESSOR(set_temp_emergency_hyst, hwmon_attr_temp_emergency_hyst)
+HWMON_GET_NUM_ACCESSOR(show_temp_lcrit, hwmon_attr_temp_lcrit)
+HWMON_SET_NUM_ACCESSOR(set_temp_lcrit, hwmon_attr_temp_lcrit)
+HWMON_GET_NUM_ACCESSOR(show_temp_offset, hwmon_attr_temp_offset)
+HWMON_SET_NUM_ACCESSOR(set_temp_offset, hwmon_attr_temp_offset)
+HWMON_GET_TEXT_ACCESSOR(show_temp_label, hwmon_attr_temp_label)
+HWMON_GET_NUM_ACCESSOR(show_temp_lowest, hwmon_attr_temp_lowest)
+HWMON_GET_NUM_ACCESSOR(show_temp_highest, hwmon_attr_temp_highest)
+HWMON_SET_NUM_ACCESSOR(set_temp_reset_hist, hwmon_attr_temp_reset_history)
+HWMON_SET_NUM_ACCESSOR(set_temp_reset_hist_glob,
+ hwmon_attr_temp_reset_history_glob)
+
+HWMON_GET_NUM_ACCESSOR(show_curr_max, hwmon_attr_curr_max)
+HWMON_SET_NUM_ACCESSOR(set_curr_max, hwmon_attr_curr_max)
+HWMON_GET_NUM_ACCESSOR(show_curr_min, hwmon_attr_curr_min)
+HWMON_SET_NUM_ACCESSOR(set_curr_min, hwmon_attr_curr_min)
+HWMON_GET_NUM_ACCESSOR(show_curr_lcrit, hwmon_attr_curr_lcrit)
+HWMON_SET_NUM_ACCESSOR(set_curr_lcrit, hwmon_attr_curr_lcrit)
+HWMON_GET_NUM_ACCESSOR(show_curr_crit, hwmon_attr_curr_crit)
+HWMON_SET_NUM_ACCESSOR(set_curr_crit, hwmon_attr_curr_crit)
+HWMON_GET_NUM_ACCESSOR(show_curr_input, hwmon_attr_curr_input)
+
+HWMON_GET_NUM_ACCESSOR(show_pow_average, hwmon_attr_pow_average)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_int, hwmon_attr_pow_average_interval)
+HWMON_SET_NUM_ACCESSOR(set_pow_average_int, hwmon_attr_pow_average_interval)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_int_max,
+ hwmon_attr_pow_average_interval_max)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_int_min,
+ hwmon_attr_pow_average_interval_min)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_highest,
+ hwmon_attr_pow_average_highest)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_lowest,
+ hwmon_attr_pow_average_lowest)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_max, hwmon_attr_pow_average_max)
+HWMON_SET_NUM_ACCESSOR(set_pow_average_max, hwmon_attr_pow_average_max)
+HWMON_GET_NUM_ACCESSOR(show_pow_average_min, hwmon_attr_pow_average_min)
+HWMON_SET_NUM_ACCESSOR(set_pow_average_min, hwmon_attr_pow_average_min)
+HWMON_GET_NUM_ACCESSOR(show_pow_input, hwmon_attr_pow_input)
+HWMON_GET_NUM_ACCESSOR(show_pow_input_highest, hwmon_attr_pow_input_highest)
+HWMON_GET_NUM_ACCESSOR(show_pow_input_lowest, hwmon_attr_pow_input_lowest)
+HWMON_SET_NUM_ACCESSOR(set_pow_reset_history, hwmon_attr_pow_reset_history)
+HWMON_GET_NUM_ACCESSOR(show_pow_accuracy, hwmon_attr_pow_accuracy)
+HWMON_GET_NUM_ACCESSOR(show_pow_cap, hwmon_attr_pow_cap)
+HWMON_SET_NUM_ACCESSOR(set_pow_cap, hwmon_attr_pow_cap)
+HWMON_GET_NUM_ACCESSOR(show_pow_cap_hyst, hwmon_attr_pow_cap_hyst)
+HWMON_SET_NUM_ACCESSOR(set_pow_cap_hyst, hwmon_attr_pow_cap_hyst)
+HWMON_GET_NUM_ACCESSOR(show_pow_cap_max, hwmon_attr_pow_cap_max)
+HWMON_GET_NUM_ACCESSOR(show_pow_cap_min, hwmon_attr_pow_cap_min)
+HWMON_GET_NUM_ACCESSOR(show_pow_max, hwmon_attr_pow_max)
+HWMON_SET_NUM_ACCESSOR(set_pow_max, hwmon_attr_pow_max)
+HWMON_GET_NUM_ACCESSOR(show_pow_crit, hwmon_attr_pow_crit)
+HWMON_SET_NUM_ACCESSOR(set_pow_crit, hwmon_attr_pow_crit)
+
+HWMON_GET_NUM_ACCESSOR(show_energy_input, hwmon_attr_ener_input)
+
+HWMON_GET_NUM_ACCESSOR(show_humidity_input, hwmon_attr_humi_input)
+
+/* functions to build/destroy sysfs entries from given hwmon caps */
+
+static int new_sysfs_entry(const char *name, mode_t mode,
+ ssize_t (*show)(struct device *, struct device_attribute *, char *),
+ ssize_t (*store)(struct device *, struct device_attribute *,
+ const char *, size_t),
+ int index, struct list_head *list, struct device *dev)
+{
+ struct hwmon_device_attribute *hw_dev_attr;
+ int ret = 0;
+
+ hw_dev_attr = kzalloc(sizeof(struct hwmon_device_attribute), GFP_KERNEL);
+ if(!hw_dev_attr)
+ return -ENOMEM;
+
+ strlcpy(hw_dev_attr->name, name, 32);
+
+ hw_dev_attr->sensor_dev.dev_attr.attr.name = hw_dev_attr->name;
+ hw_dev_attr->sensor_dev.dev_attr.attr.mode = mode;
+ hw_dev_attr->sensor_dev.dev_attr.show = show;
+ hw_dev_attr->sensor_dev.dev_attr.store = store;
+ hw_dev_attr->sensor_dev.index = index;
+ hw_dev_attr->hw_dev_inst = dev_get_drvdata(dev);
+ sysfs_attr_init(&hw_dev_attr->sensor_dev.dev_attr.attr);
+
+ ret = device_create_file(dev->parent, &hw_dev_attr->sensor_dev.dev_attr);
+ if(ret) {
+ kfree(hw_dev_attr);
+ return ret;
+ }
+
+ list_add(&hw_dev_attr->node, list);
+
+ return 0;
+}
+
+int hwmon_create_sysfs(struct device *dev)
+{
+ struct hwmon_device_instance *hw_dev = dev_get_drvdata(dev);
+ struct hwmon_device_caps *caps = &hw_dev->caps;
+ char attr_name[HWMON_NAME_SIZE];
+ int i;
+
+ INIT_LIST_HEAD(&hw_dev->sysfs_node);
+
+ new_sysfs_entry("name", S_IRUGO, &show_name, NULL, 0,
+ &hw_dev->sysfs_node, dev);
+ new_sysfs_entry("update_interval", S_IRUGO, &show_update_interval, NULL, 0,
+ &hw_dev->sysfs_node, dev);
+
+ /* voltage sysfs entries */
+ for(i = 1; i <= caps->num_voltage; i++) {
+ if(caps->volt_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "in%u_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_in_min,
+ &set_in_min, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->volt_lcrit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "in%u_lcrit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_in_lcrit,
+ &set_in_lcrit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->volt_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "in%u_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_in_max,
+ &set_in_max, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->volt_crit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "in%u_crit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_in_crit,
+ &set_in_crit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->volt_vid) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "cpu%u_vid", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_in_vid,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->volt_label) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "in%u_label", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_in_label,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ snprintf(attr_name, HWMON_NAME_SIZE, "in%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_in_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->num_voltage && caps->volt_vrm) {
+ if(new_sysfs_entry("in_vrm", S_IRUGO, &show_in_vrm,
+ NULL, 0, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+
+ /* fan sysfs entries */
+ for(i = 1; i <= caps->num_fan; i++) {
+ if(caps->fan_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_fan_min,
+ &set_fan_min, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->fan_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_fan_max,
+ &set_fan_max, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->fan_div) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_div", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_fan_div,
+ &set_fan_div, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->fan_pulses) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_pulses", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_fan_pulses,
+ &set_fan_pulses, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->fan_target) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_target", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_fan_target,
+ &set_fan_target, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->fan_label) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_label", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_fan_label,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ snprintf(attr_name, HWMON_NAME_SIZE, "fan%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_fan_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+
+ /* pwm sysfs entries */
+ for(i = 1; i <= caps->num_pwm; i++) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "pwm%u", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pwm,
+ &set_pwm, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ snprintf(attr_name, HWMON_NAME_SIZE, "pwm%u_enable", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pwm_enable,
+ &set_pwm_enable, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ snprintf(attr_name, HWMON_NAME_SIZE, "pwm%u_mode", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pwm_mode,
+ &set_pwm_mode, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ if(caps->pwm_freq) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "pwm%u_freq", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pwm_freq,
+ &set_pwm_freq, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pwm_auto_channels_temp) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "pwm%u_auto_channels_temp", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pwm_auto_ct,
+ &set_pwm_auto_ct, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ }
+
+ /* temp sysfs entries */
+ for(i = 1; i <= caps->num_temp; i++) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_temp_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ if(caps->temp_type) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_type", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_type,
+ &set_temp_type, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_max,
+ &set_temp_max, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_min,
+ &set_temp_min, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_max_hyst) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_max_hyst", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_max_hyst,
+ &set_temp_max_hyst, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_crit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_crit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_crit,
+ &set_temp_crit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_crit_hyst) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_crit_hyst", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO,
+ &show_temp_crit_hyst, &set_temp_crit_hyst, i,
+ &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_emergency) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_emergency", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO,
+ &show_temp_emergency, &set_temp_emergency, i,
+ &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_emergency_hyst) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_emergency_hyst", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO,
+ &show_temp_emergency_hyst, &set_temp_emergency_hyst, i,
+ &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_lcrit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_lcrit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_lcrit,
+ &set_temp_lcrit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_offset) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_offset", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_temp_offset,
+ &set_temp_offset, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_label) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_label", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_temp_label,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_lowest) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_lowest", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_temp_lowest,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_highest) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_highest", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_temp_highest,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->temp_reset_history) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "temp%u_reset_history", i);
+ if(new_sysfs_entry(attr_name, S_IWUGO, NULL,
+ &set_temp_reset_hist, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ }
+ if(caps->num_temp && caps->temp_reset_history) {
+ if(new_sysfs_entry("temp_reset_history", S_IWUGO, NULL,
+ &set_temp_reset_hist_glob, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+
+ /* current sysfs entries */
+ for(i = 1; i <= caps->num_current; i++) {
+ if(caps->curr_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "curr%u_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_curr_max,
+ &set_curr_max, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->curr_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "curr%u_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_curr_min,
+ &set_curr_min, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->curr_lcrit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "curr%u_lcrit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_curr_lcrit,
+ &set_curr_lcrit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->curr_crit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "curr%u_crit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_curr_crit,
+ &set_curr_crit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ snprintf(attr_name, HWMON_NAME_SIZE, "curr%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_curr_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+
+ /* power sysfs entries */
+ for(i = 1; i <= caps->num_power; i++) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ if(caps->pow_average) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_average", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_average,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_interval) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_average_interval", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO,
+ &show_pow_average_int, &set_pow_average_int, i,
+ &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_interval_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE,
+ "power%u_average_interval_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_average_int_max,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_interval_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE,
+ "power%u_average_interval_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_average_int_min,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_highest) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_average_highest", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_average_highest,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_lowest) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_average_lowest", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_average_lowest,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_average_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO,
+ &show_pow_average_max, &set_pow_average_max, i,
+ &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_average_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_average_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO,
+ &show_pow_average_min, &set_pow_average_min, i,
+ &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_input_highest) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_input_highest", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_input_highest,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_input_lowest) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_input_lowest", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_input_lowest,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_reset_history) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_reset_history", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, NULL,
+ &set_pow_reset_history, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_accuracy) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_accuracy", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_accuracy,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_cap) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_cap", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pow_cap,
+ &set_pow_cap, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_cap_hyst) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_cap_hyst", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pow_cap_hyst,
+ &set_pow_cap_hyst, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_cap_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_cap_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_cap_max,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_cap_min) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_cap_min", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_pow_cap_min,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_max) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_max", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pow_max,
+ &set_pow_max, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ if(caps->pow_crit) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "power%u_crit", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO|S_IWUGO, &show_pow_crit,
+ &set_pow_crit, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+
+ /* energy sysfs entries */
+ for(i = 1; i <= caps->num_energy; i++) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "energy%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_energy_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+
+ /* humidity sysfs entries */
+ for(i = 1; i <= caps->num_humidity; i++) {
+ snprintf(attr_name, HWMON_NAME_SIZE, "humidity%u_input", i);
+ if(new_sysfs_entry(attr_name, S_IRUGO, &show_humidity_input,
+ NULL, i, &hw_dev->sysfs_node, dev))
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ hwmon_destroy_sysfs(dev);
+ return -EAGAIN;
+}
+
+void hwmon_destroy_sysfs(struct device *dev)
+{
+ struct hwmon_device_instance *hw_dev = dev_get_drvdata(dev);
+ struct hwmon_device_attribute *hw_dev_attr, *tmp;
+
+ list_for_each_entry_safe(hw_dev_attr, tmp, &hw_dev->sysfs_node, node) {
+ device_remove_file(dev, &hw_dev_attr->sensor_dev.dev_attr);
+ list_del(&hw_dev_attr->node);
+ kfree(hw_dev_attr);
+ }
+}
+
+EXPORT_SYMBOL_GPL(hwmon_create_sysfs);
+EXPORT_SYMBOL_GPL(hwmon_destroy_sysfs);
+
+MODULE_AUTHOR("Lucas Stach <dev at lynxeye.de>");
+MODULE_DESCRIPTION("hardware monitoring core api support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/hwmon-core.h b/include/linux/hwmon-core.h
new file mode 100644
index 0000000..270ad67
--- /dev/null
+++ b/include/linux/hwmon-core.h
@@ -0,0 +1,211 @@
+/**
+ * hwmon-core.h
+ * Copyright (C) 2011 Lucas Stach
+ *
+ * hwmon-core interface definitions
+ *
+ * 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.
+ */
+
+#ifndef HWMON_CORE_H_
+#define HWMON_CORE_H_
+
+struct hwmon_device;
+
+int hwmon_create_sysfs(struct device *dev);
+
+void hwmon_destroy_sysfs(struct device *dev);
+
+enum hwmon_numeric_attr {
+ hwmon_attr_volt_min,
+ hwmon_attr_volt_lcrit,
+ hwmon_attr_volt_max,
+ hwmon_attr_volt_crit,
+ hwmon_attr_volt_input,
+ hwmon_attr_volt_vid,
+ hwmon_attr_volt_vrm,
+
+ hwmon_attr_fan_min,
+ hwmon_attr_fan_max,
+ hwmon_attr_fan_input,
+ hwmon_attr_fan_div,
+ hwmon_attr_fan_pulses,
+ hwmon_attr_fan_target,
+
+ hwmon_attr_pwm,
+ hwmon_attr_pwm_enable,
+ hwmon_attr_pwm_mode,
+ hwmon_attr_pwm_freq,
+ hwmon_attr_pwm_auto_channels_temp,
+ hwmon_attr_pwm_auto_point_pwm,
+ hwmon_attr_pwm_auto_point_temp,
+ hwmon_attr_pwm_auto_point_temp_hyst,
+
+ hwmon_attr_temp_type,
+ hwmon_attr_temp_max,
+ hwmon_attr_temp_max_hyst,
+ hwmon_attr_temp_min,
+ hwmon_attr_temp_input,
+ hwmon_attr_temp_crit,
+ hwmon_attr_temp_crit_hyst,
+ hwmon_attr_temp_emergency,
+ hwmon_attr_temp_emergency_hyst,
+ hwmon_attr_temp_lcrit,
+ hwmon_attr_temp_offset,
+ hwmon_attr_temp_lowest,
+ hwmon_attr_temp_highest,
+ hwmon_attr_temp_reset_history,
+ hwmon_attr_temp_reset_history_glob,
+ hwmon_attr_temp_auto_point_pwm,
+ hwmon_attr_temp_auto_point_temp,
+ hwmon_attr_temp_auto_point_hyst,
+
+ hwmon_attr_curr_max,
+ hwmon_attr_curr_min,
+ hwmon_attr_curr_lcrit,
+ hwmon_attr_curr_crit,
+ hwmon_attr_curr_input,
+
+ hwmon_attr_pow_average,
+ hwmon_attr_pow_average_interval,
+ hwmon_attr_pow_average_interval_max,
+ hwmon_attr_pow_average_interval_min,
+ hwmon_attr_pow_average_highest,
+ hwmon_attr_pow_average_lowest,
+ hwmon_attr_pow_average_max,
+ hwmon_attr_pow_average_min,
+ hwmon_attr_pow_input,
+ hwmon_attr_pow_input_highest,
+ hwmon_attr_pow_input_lowest,
+ hwmon_attr_pow_reset_history,
+ hwmon_attr_pow_accuracy,
+ hwmon_attr_pow_cap,
+ hwmon_attr_pow_cap_hyst,
+ hwmon_attr_pow_cap_min,
+ hwmon_attr_pow_cap_max,
+ hwmon_attr_pow_max,
+ hwmon_attr_pow_crit,
+
+ hwmon_attr_ener_input,
+
+ hwmon_attr_humi_input,
+
+ hwmon_attr_intr_alarm,
+ hwmon_attr_intr_beep,
+
+ hwmon_attr_beep,
+
+ hwmon_attr_update_interval
+};
+
+enum hwmon_text_attr {
+ hwmon_attr_name,
+ hwmon_attr_volt_label,
+ hwmon_attr_fan_label,
+ hwmon_attr_temp_label,
+};
+
+struct hwmon_device_caps {
+ /* number of inputs */
+ unsigned int num_voltage:4;
+ unsigned int num_fan:4;
+ unsigned int num_pwm:4;
+ unsigned int num_temp:4;
+ unsigned int num_current:4;
+ unsigned int num_power:4;
+ unsigned int num_energy:4;
+ unsigned int num_humidity:4;
+ unsigned int num_intrusion:4;
+
+ unsigned int num_trip_points:4;
+
+ /* voltage caps */
+ unsigned int volt_min:1;
+ unsigned int volt_lcrit:1;
+ unsigned int volt_max:1;
+ unsigned int volt_crit:1;
+ unsigned int volt_label:1;
+ unsigned int volt_vid:1;
+ unsigned int volt_vrm:1;
+
+ /* fan caps */
+ unsigned int fan_min:1;
+ unsigned int fan_max:1;
+ unsigned int fan_div:1;
+ unsigned int fan_pulses:1;
+ unsigned int fan_target:1;
+ unsigned int fan_label:1;
+
+ /* pwm caps */
+ unsigned int pwm_freq:1;
+ unsigned int pwm_auto_channels_temp:1;
+ unsigned int pwm_auto_point_pwm:1;
+ unsigned int pwm_auto_point_temp:1;
+ unsigned int pwm_auto_point_temp_hyst:1;
+
+ /* temp caps */
+ unsigned int temp_type:1;
+ unsigned int temp_min:1;
+ unsigned int temp_max:1;
+ unsigned int temp_max_hyst:1;
+ unsigned int temp_crit:1;
+ unsigned int temp_crit_hyst:1;
+ unsigned int temp_emergency:1;
+ unsigned int temp_emergency_hyst:1;
+ unsigned int temp_lcrit:1;
+ unsigned int temp_offset:1;
+ unsigned int temp_label:1;
+ unsigned int temp_lowest:1;
+ unsigned int temp_highest:1;
+ unsigned int temp_reset_history:1;
+ unsigned int temp_auto_point_pwm:1;
+ unsigned int temp_auto_point_temp:1;
+ unsigned int temp_auto_point_temp_hyst:1;
+
+ /* current caps */
+ unsigned int curr_max:1;
+ unsigned int curr_min:1;
+ unsigned int curr_lcrit:1;
+ unsigned int curr_crit:1;
+
+ /* power caps */
+ unsigned int pow_average:1;
+ unsigned int pow_average_interval:1;
+ unsigned int pow_average_interval_min:1;
+ unsigned int pow_average_interval_max:1;
+ unsigned int pow_average_min:1;
+ unsigned int pow_average_max:1;
+ unsigned int pow_average_lowest:1;
+ unsigned int pow_average_highest:1;
+ unsigned int pow_input_lowest:1;
+ unsigned int pow_input_highest:1;
+ unsigned int pow_reset_history:1;
+ unsigned int pow_accuracy:1;
+ unsigned int pow_cap:1;
+ unsigned int pow_cap_hyst:1;
+ unsigned int pow_cap_min:1;
+ unsigned int pow_cap_max:1;
+ unsigned int pow_max:1;
+ unsigned int pow_crit:1;
+
+ /* alarm caps */
+ unsigned int alarm_channel:1;
+ unsigned int alarm_limit:1;
+};
+
+
+struct hwmon_device_instance {
+ struct hwmon_device_caps caps;
+ int (*get_numeric_attr) (void * inst_data, enum hwmon_numeric_attr attr,
+ unsigned int index, int *value);
+ int (*get_text_attr) (void * inst_data, enum hwmon_text_attr attr,
+ unsigned int index, char *buf);
+ int (*set_numeric_attr) (void * inst_data, enum hwmon_numeric_attr attr,
+ unsigned int index, int value);
+ struct list_head sysfs_node;
+ void *inst_data;
+};
+
+#endif /* HWMON_CORE_H_ */
--
1.7.5.4
More information about the lm-sensors
mailing list