Re: [RFC 1/2] add new hwmon core interface

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

 



On 07/01/11 00:44, Lucas Stach wrote:
> 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.
In principal this is quite similar to what we did recently with IIO_CHAN_SPEC
in iio, but clearly your approach is somewhat different.  Perhaps it is worth
us both reading each others code to see if we can make suggestions.

Based on an initial look my main suggestion is to set it up so these capabilities
can be specified as static const structure in the drivers. Makes for easier to read
and smaller code.  Actually I can see no reason why you can't do this in your
example driver.  As far as I can tell these are shared across multiple instances
of the driver anyway.

Various comments inline.  Basically they boil down to the fact this code could
be much shorter with a few minor tweaks to how things are configured.


> ---
>  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; \
> +	}
> +
Why not just encapsulate the enum into a structure then you will only need one accessor
for each type of reading?  Just use a sensor_device_attribute and put it in the index
field.  Lots less code and same result.  Also means you can drop the function pointers
in my suggestion below.  Whilst you currently pass on the attr->index value I can't
see any where it is actually set?


> +/* 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++) {

Hmm. the approach of lots of separate bit fields you have used in hwmon_device_caps
does make this code overly repetative.  Perhaps having
a single voltage capabilities field, and using a bit mask would allow you to do
this as a trivial use of a magic table.

Say we have

unsigned int volt_caps:8;
enum {
     min,
     lcrit,
     max,
     crit,
     label,
     vid,
     vrm,
} volt_caps;

enum {
     text,
     value,
} cap_type;
struct cap_info {
       const char *format_string;
       enum cap_type;
       /* some function pointers */
}

struct cap_info[] = {
       [min] = { "in%u_min", value, &show_in_min, &set_in_min },
       [lcrit] = { "in%u_lcrit", value, &show_in_lcrit, &set_in_lcrit },
...
};

The the following just becomes a for_each_bit_set() against that structure.

With a little extension of this principal, all the different channel types
can be handled as well.


> +		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,
> +};
> +
This structure is rather complex, and as far as I can tell
puts lots of arbitary limits on numbers of channels.

> +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_ */


_______________________________________________
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