[lm-sensors] TODO: "dynamic" sysfs callbacks

Jim Cromie jim.cromie at gmail.com
Sun Aug 27 20:34:30 CEST 2006


once again with the email-harvesting / communication/coordination.

<quoting Jean, I think>

BTW, do you have any plan to convert the asb100 driver to use the
"dynamic" sysfs callbacks? This would kill some more macros, and shrink
the driver size even more.


The approx list of 'opportunities' here is:

[jimc at harpo ac-2]$ grep -rl '##' drivers/hwmon/*.c
drivers/hwmon/abituguru.c
drivers/hwmon/adm1021.c
drivers/hwmon/adm1025.c
drivers/hwmon/adm1026.c	 - patch on hold, needs tester.
drivers/hwmon/adm1031.c
drivers/hwmon/adm9240.c
drivers/hwmon/asb100.c
drivers/hwmon/ds1621.c
drivers/hwmon/fscher.c
drivers/hwmon/fscpos.c
drivers/hwmon/gl518sm.c
drivers/hwmon/gl520sm.c
drivers/hwmon/it87.c
drivers/hwmon/lm75.c
drivers/hwmon/lm77.c
drivers/hwmon/lm78.c
drivers/hwmon/lm80.c
drivers/hwmon/lm85.c
drivers/hwmon/lm87.c
drivers/hwmon/lm92.c
drivers/hwmon/max1619.c
drivers/hwmon/sis5595.c
drivers/hwmon/smsc47b397.c
drivers/hwmon/smsc47m192.c
drivers/hwmon/smsc47m1.c
drivers/hwmon/via686a.c
drivers/hwmon/vt8231.c
drivers/hwmon/w83627ehf.c
drivers/hwmon/w83627hf.c
drivers/hwmon/w83781d.c
drivers/hwmon/w83791d.c
drivers/hwmon/w83792d.c



"dynamic" sysfs callbacks is :


current code base has lots of macros which are essentially
static inline functions, but which are 'parameterized' by
'inserting' an ##offset## into the function name, and 'invoking'
the macro repeatedly for multiple instances of a sensor.

an example should clarify. (from w83781d.c, chosen arbitrarily)

    341 static ssize_t store_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, const
    341 char *buf, size_t count) \
    342 { \
    343         return store_in_##reg (dev, buf, count, offset); \
    344 } \
    345 static DEVICE_ATTR(in##offset##_##reg, S_IRUGO| S_IWUSR, show_regs_in_##reg##offset, store_regs_in_##reg##offset);
    346
    347 #define sysfs_in_offsets(offset) \
    348 sysfs_in_offset(offset); \
    349 sysfs_in_reg_offset(min, offset); \
    350 sysfs_in_reg_offset(max, offset);
    351
    352 sysfs_in_offsets(0);
    353 sysfs_in_offsets(1);
    354 sysfs_in_offsets(2);
    355 sysfs_in_offsets(3);
    356 sysfs_in_offsets(4);
    357 sysfs_in_offsets(5);
    358 sysfs_in_offsets(6);
    359 sysfs_in_offsets(7);
    360 sysfs_in_offsets(8);

Once these macros are expanded and compiled, we get a multitude
of nearly identical routines.

[jimc at harpo hwmon]$ nm  -S w83781d.o | grep store_regs_
00001461 00000011 t store_regs_beep_enable
00001472 00000011 t store_regs_beep_mask
00002971 00000011 t store_regs_fan_div_1
00002982 00000011 t store_regs_fan_div_2
00002993 00000011 t store_regs_fan_div_3
00000589 00000011 t store_regs_fan_min1
0000059a 00000011 t store_regs_fan_min2
000005ab 00000011 t store_regs_fan_min3
00000428 00000011 t store_regs_in_max0
00000439 00000011 t store_regs_in_max1
0000044a 00000011 t store_regs_in_max2
0000045b 00000011 t store_regs_in_max3
0000046c 00000011 t store_regs_in_max4
0000047d 00000011 t store_regs_in_max5
0000048e 00000011 t store_regs_in_max6
0000049f 00000011 t store_regs_in_max7
000004b0 00000011 t store_regs_in_max8
000002f6 00000011 t store_regs_in_min0
00000307 00000011 t store_regs_in_min1
00000318 00000011 t store_regs_in_min2
00000329 00000011 t store_regs_in_min3
0000033a 00000011 t store_regs_in_min4
0000034b 00000011 t store_regs_in_min5
0000035c 00000011 t store_regs_in_min6
0000036d 00000011 t store_regs_in_min7
0000037e 00000011 t store_regs_in_min8


The macros can be converted to proper static inlines,
and parameterized properly so that 1 func does the job for all 8
inputs above.

static ssize_t show_fan_min(struct device *dev, struct device_attribute *devattr, char *buf)
{

	/*THIS IS THE KEY*/
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
	int index = attr->index;

        struct pc87360_data *data = pc87360_update_device(dev);
        return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[index],
                       FAN_DIV_FROM_REG(data->fan_status[index])));
}

static struct sensor_device_attribute fan_min[] = {
        SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 0),
        SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 1),
        SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2),
};


There are 2 things going on here.

1 - the function back-casts from a base-type to another that 
aggregates another parameter (index), which is then available
to distinguish which of many (fan) sensors is being requested.

2 - the SENSOR_ATTR initializes the index field (0,1,2 above)
So when the function is called, it can use that index.




*Deeper 'dynamic's are possible*

The above folds multiple callbacks on 1 dimension (index)
to reduce code footprint, but more are possible.



following 2 steps above:

1. function now relys on being passed a struct sensor_device_attribute_2
which has 2 additional parameters; index, and nr.  This example uses
2nd parameter to distinguish which attribute of the sensor is being
requested (index still chooses 1 of N sensors)
 
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, char *buf)
{
        struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
        int idx = attr->index;
        int func = attr->nr;
        struct pc87360_data *data = pc87360_update_device(dev);
        unsigned res = -1;

        switch(func) {
        case FN_FAN_INPUT:
                res = FAN_FROM_REG(data->fan[idx],
                                   FAN_DIV_FROM_REG(data->fan_status[idx]));
                break;
        case FN_FAN_MIN:
                res = FAN_FROM_REG(data->fan_min[idx],
                                   FAN_DIV_FROM_REG(data->fan_status[idx]));
                break;
        case FN_FAN_STATUS:
                res = FAN_STATUS_FROM_REG(data->fan_status[idx]);
                break;
        case FN_FAN_DIV:
                res = FAN_DIV_FROM_REG(data->fan_status[idx]);
                break;
        default:
                printk(KERN_ERR "unknown attr fetch\n");
        }
        return sprintf(buf, "%u\n", res);
}


2.  declare attributes like so.
Note that show_fan is the callback for both fan_input[]s, and fan_status[]s,
as well as the others (DIV,MIN) above.

static struct sensor_device_attribute_2 fan_input[] = {
        SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, FN_FAN_INPUT, 0),
        SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, FN_FAN_INPUT, 1),
        SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, FN_FAN_INPUT, 2),
};
static struct sensor_device_attribute_2 fan_status[] = {
        SENSOR_ATTR_2(fan1_status, S_IRUGO, show_fan, NULL, FN_FAN_STATUS, 0),
        SENSOR_ATTR_2(fan2_status, S_IRUGO, show_fan, NULL, FN_FAN_STATUS, 1),
        SENSOR_ATTR_2(fan3_status, S_IRUGO, show_fan, NULL, FN_FAN_STATUS, 2),
};


NB - show, set routines can never be combined, due to different fn-signatures.
NB - technique saves ~ 9% object code on 1 driver.


This "2-D dynamic callback" approach is not widely used,
currently only done in:
	w83792d.c, abituguru.c.
patches which use it:
	vt1211.c - going in soon I believe (congrats Juerg)
	pc87360.c - 
	- unreviewed, patch 3/3 has some annoying screwups in the attr mapping,
	- as visible above (DIV vs fan_status, will recheck/redo)
	case FN_FAN_DIV:
                res = FAN_DIV_FROM_REG(data->fan_status[idx]);









More information about the lm-sensors mailing list