On 07/01/11 10:37, Jonathan Cameron wrote: > 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? Sorry, I see you are using that for channel index. Use the two parameter form of sensor_device_attribute_2 instead and the point still holds. > > >> +/* 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 > _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors