[RFC 1/2] add new hwmon core interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



>From 94262a505a888fdb04cb1f37ffb33e240a58484f Mon Sep 17 00:00:00 2001
From: Lucas Stach <dev@xxxxxxxxxx>
Date: Fri, 1 Jul 2011 00:55:30 +0200
Subject: [PATCH 1/2] hwmon: add hwmon-core interface

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@xxxxxxxxxx>");
+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



_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors


[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux