root/lm-sensors/branches/lm-sensors-3.0.0/lib/sysfs.c @ 4769

Revision 4769, 12.3 KB (checked in by khali, 7 years ago)

Add support for temperature offset files, which are part of the
standard sysfs interface. I don't think it makes much sense to
display the offset values as part of the output of "sensors" (it
would even probably confuse people) but having support for these
in libsensors makes it possible to adjust the offsets in
sensors.conf, which is convenient.

This closes ticket #2248.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    sysfs.c - Part of libsensors, a library for reading Linux sensor data
3    Copyright (c) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
4    Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21/* this define needed for strndup() */
22#define _GNU_SOURCE
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdlib.h>
29#include <limits.h>
30#include <errno.h>
31#include <sysfs/libsysfs.h>
32#include "data.h"
33#include "error.h"
34#include "access.h"
35#include "general.h"
36#include "sysfs.h"
37
38char sensors_sysfs_mount[NAME_MAX];
39
40#define MAX_SENSORS_PER_TYPE    20
41#define MAX_SUB_FEATURES        7
42/* Room for all 3 types (in, fan, temp) with all their subfeatures + VID
43   + misc features */
44#define ALL_POSSIBLE_FEATURES   (MAX_SENSORS_PER_TYPE * MAX_SUB_FEATURES * 6 \
45                                 + MAX_SENSORS_PER_TYPE + 1)
46
47static
48int get_type_scaling(int type)
49{
50        switch (type & 0xFF10) {
51        case SENSORS_FEATURE_IN:
52        case SENSORS_FEATURE_TEMP:
53                return 1000;
54        case SENSORS_FEATURE_FAN:
55                return 1;
56        }
57
58        switch (type) {
59        case SENSORS_FEATURE_VID:
60        case SENSORS_FEATURE_TEMP_OFFSET:
61                return 1000;
62        default:
63                return 1;
64        }
65}
66
67static int sensors_read_dynamic_chip(sensors_chip_features *chip,
68                                     struct sysfs_device *sysdir)
69{
70        int i, type, fnum = 0;
71        struct sysfs_attribute *attr;
72        struct dlist *attrs;
73        sensors_chip_feature *features;
74        sensors_chip_feature *dyn_features;
75        char *name;
76
77        attrs = sysfs_get_device_attributes(sysdir);
78
79        if (attrs == NULL)
80                return -ENOENT;
81
82        /* We use a large sparse table at first to store all found features,
83           so that we can store them sorted at type and index and then later
84           create a dense sorted table. */
85        features = calloc(ALL_POSSIBLE_FEATURES, sizeof(sensors_chip_feature));
86        if (!features)
87                sensors_fatal_error(__FUNCTION__, "Out of memory");
88
89        dlist_for_each_data(attrs, attr, struct sysfs_attribute) {
90                name = attr->name;
91                int nr;
92
93                type = sensors_feature_get_type(name, &nr);
94                if (type == SENSORS_FEATURE_UNKNOWN)
95                        continue;
96
97                /* Adjust the channel number */
98                switch (type & 0xFF00) {
99                        case SENSORS_FEATURE_FAN:
100                        case SENSORS_FEATURE_TEMP:
101                                if (nr)
102                                        nr--;
103                                break;
104                }
105
106                if (nr >= MAX_SENSORS_PER_TYPE) {
107                        fprintf(stderr, "libsensors error, more sensors of one"
108                                " type then MAX_SENSORS_PER_TYPE, ignoring "
109                                "feature: %s\n", name);
110                        continue;
111                }
112
113                /* "calculate" a place to store the feature in our sparse,
114                   sorted table */
115                switch (type) {
116                case SENSORS_FEATURE_VID:
117                        i = nr + MAX_SENSORS_PER_TYPE * MAX_SUB_FEATURES * 6;
118                        break;
119                case SENSORS_FEATURE_BEEP_ENABLE:
120                        i = MAX_SENSORS_PER_TYPE * MAX_SUB_FEATURES * 6 +
121                            MAX_SENSORS_PER_TYPE;
122                        break;
123                default:
124                        i = (type >> 8) * MAX_SENSORS_PER_TYPE *
125                            MAX_SUB_FEATURES * 2 + nr * MAX_SUB_FEATURES * 2 +
126                            ((type & 0x10) >> 4) * MAX_SUB_FEATURES +
127                            (type & 0x0F);
128                }
129
130                if (features[i].data.name) {
131                        fprintf(stderr, "libsensors error, trying to add dupli"
132                                "cate feature: %s to dynamic feature table\n",
133                                name);
134                        continue;
135                }
136
137                /* fill in the feature members */
138                features[i].data.type = type;
139
140                /* check for _input extension and remove */
141                nr = strlen(name);
142                if (nr > 6 && !strcmp(name + nr - 6, "_input"))
143                        features[i].data.name = strndup(name, nr - 6);
144                else
145                        features[i].data.name = strdup(name);
146
147                if ((type & 0x00FF) == 0) {
148                        /* main feature */
149                        features[i].data.mapping = SENSORS_NO_MAPPING;
150                } else {
151                        /* sub feature */
152                        /* The mapping is set below after numbering */
153                        if (!(type & 0x10))
154                                features[i].data.flags |= SENSORS_COMPUTE_MAPPING;
155                }
156
157                if (attr->method & SYSFS_METHOD_SHOW)
158                        features[i].data.flags |= SENSORS_MODE_R;
159                if (attr->method & SYSFS_METHOD_STORE)
160                        features[i].data.flags |= SENSORS_MODE_W;
161
162                fnum++;
163        }
164
165        if (!fnum) { /* No feature */
166                chip->feature = NULL;
167                goto exit_free;
168        }
169
170        dyn_features = calloc(fnum, sizeof(sensors_chip_feature));
171        if (dyn_features == NULL) {
172                sensors_fatal_error(__FUNCTION__, "Out of memory");
173        }
174
175        fnum = 0;
176        for (i = 0; i < ALL_POSSIBLE_FEATURES; i++) {
177                if (features[i].data.name) {
178                        dyn_features[fnum] = features[i];
179                        fnum++;
180                }
181        }
182
183        /* Number the features linearly, so that feature number N is at
184           position N in the array. This allows for O(1) look-ups. */
185        for (i = 0; i < fnum; i++) {
186                int j;
187
188                dyn_features[i].data.number = i;
189                if (dyn_features[i].data.mapping == SENSORS_NO_MAPPING) {
190                        /* Main feature, set the mapping field of all its
191                           subfeatures */
192                        for (j = i + 1; j < fnum &&
193                             dyn_features[j].data.mapping != SENSORS_NO_MAPPING;
194                             j++)
195                                dyn_features[j].data.mapping = i;
196                }
197        }
198
199        chip->feature = dyn_features;
200        chip->feature_count = fnum;
201
202exit_free:
203        free(features);
204        return 0;
205}
206
207/* returns !0 if sysfs filesystem was found, 0 otherwise */
208int sensors_init_sysfs(void)
209{
210        struct stat statbuf;
211
212        /* libsysfs will return success even if sysfs is not mounted,
213           so we have to double-check */
214        if (sysfs_get_mnt_path(sensors_sysfs_mount, NAME_MAX)
215         || stat(sensors_sysfs_mount, &statbuf) < 0
216         || statbuf.st_nlink <= 2)      /* Empty directory */
217                return 0;
218
219        return 1;
220}
221
222/* returns: 0 if successful, !0 otherwise */
223static int sensors_read_one_sysfs_chip(struct sysfs_device *dev)
224{
225        int domain, bus, slot, fn;
226        int err = -SENSORS_ERR_PARSE;
227        struct sysfs_attribute *attr, *bus_attr;
228        char bus_path[SYSFS_PATH_MAX];
229        sensors_chip_features entry;
230
231        /* ignore any device without name attribute */
232        if (!(attr = sysfs_get_device_attr(dev, "name")))
233                return 0;
234
235        /* NB: attr->value[attr->len-1] == '\n'; chop that off */
236        entry.chip.prefix = strndup(attr->value, attr->len - 1);
237        if (!entry.chip.prefix)
238                sensors_fatal_error(__FUNCTION__, "out of memory");
239
240        entry.chip.path = strdup(dev->path);
241        if (!entry.chip.path)
242                sensors_fatal_error(__FUNCTION__, "out of memory");
243
244        if (sscanf(dev->name, "%hd-%x", &entry.chip.bus.nr, &entry.chip.addr) == 2) {
245                /* find out if legacy ISA or not */
246                if (entry.chip.bus.nr == 9191) {
247                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
248                        entry.chip.bus.nr = 0;
249                } else {
250                        entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
251                        snprintf(bus_path, sizeof(bus_path),
252                                "%s/class/i2c-adapter/i2c-%d/device/name",
253                                sensors_sysfs_mount, entry.chip.bus.nr);
254
255                        if ((bus_attr = sysfs_open_attribute(bus_path))) {
256                                if (sysfs_read_attribute(bus_attr)) {
257                                        sysfs_close_attribute(bus_attr);
258                                        goto exit_free;
259                                }
260
261                                if (bus_attr->value
262                                 && !strncmp(bus_attr->value, "ISA ", 4)) {
263                                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
264                                        entry.chip.bus.nr = 0;
265                                }
266
267                                sysfs_close_attribute(bus_attr);
268                        }
269                }
270        } else if (sscanf(dev->name, "spi%hd.%d", &entry.chip.bus.nr,
271                          &entry.chip.addr) == 2) {
272                /* SPI */
273                entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
274        } else if (sscanf(dev->name, "%*[a-z0-9_].%d", &entry.chip.addr) == 1) {
275                /* must be new ISA (platform driver) */
276                entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
277                entry.chip.bus.nr = 0;
278        } else if (sscanf(dev->name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
279                /* PCI */
280                entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
281                entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
282                entry.chip.bus.nr = 0;
283        } else
284                goto exit_free;
285
286        if (sensors_read_dynamic_chip(&entry, dev) < 0)
287                goto exit_free;
288        if (!entry.feature) { /* No feature, discard chip */
289                err = 0;
290                goto exit_free;
291        }
292        sensors_add_proc_chips(&entry);
293
294        return 0;
295
296exit_free:
297        free(entry.chip.prefix);
298        free(entry.chip.path);
299        return err;
300}
301
302/* returns 0 if successful, !0 otherwise */
303static int sensors_read_sysfs_chips_compat(void)
304{
305        struct sysfs_bus *bus;
306        struct dlist *devs;
307        struct sysfs_device *dev;
308        int ret = 0;
309
310        if (!(bus = sysfs_open_bus("i2c"))) {
311                if (errno && errno != ENOENT)
312                        ret = -SENSORS_ERR_PROC;
313                goto exit0;
314        }
315
316        if (!(devs = sysfs_get_bus_devices(bus))) {
317                if (errno && errno != ENOENT)
318                        ret = -SENSORS_ERR_PROC;
319                goto exit1;
320        }
321
322        dlist_for_each_data(devs, dev, struct sysfs_device)
323                if ((ret = sensors_read_one_sysfs_chip(dev)))
324                        goto exit1;
325
326exit1:
327        /* this frees bus and devs */
328        sysfs_close_bus(bus);
329
330exit0:
331        return ret;
332}
333
334/* returns 0 if successful, !0 otherwise */
335int sensors_read_sysfs_chips(void)
336{
337        struct sysfs_class *cls;
338        struct dlist *clsdevs;
339        struct sysfs_class_device *clsdev;
340        int ret = 0;
341
342        if (!(cls = sysfs_open_class("hwmon"))) {
343                /* compatibility function for kernel 2.6.n where n <= 13 */
344                return sensors_read_sysfs_chips_compat();
345        }
346
347        if (!(clsdevs = sysfs_get_class_devices(cls))) {
348                if (errno && errno != ENOENT)
349                        ret = -SENSORS_ERR_PROC;
350                goto exit;
351        }
352
353        dlist_for_each_data(clsdevs, clsdev, struct sysfs_class_device) {
354                struct sysfs_device *dev;
355                if (!(dev = sysfs_get_classdev_device(clsdev))) {
356                        ret = -SENSORS_ERR_PROC;
357                        goto exit;
358                }
359                if ((ret = sensors_read_one_sysfs_chip(dev)))
360                        goto exit;
361        }
362
363exit:
364        /* this frees cls and clsdevs */
365        sysfs_close_class(cls);
366        return ret;
367}
368
369/* returns 0 if successful, !0 otherwise */
370int sensors_read_sysfs_bus(void)
371{
372        struct sysfs_class *cls;
373        struct dlist *clsdevs;
374        struct sysfs_class_device *clsdev;
375        sensors_bus entry;
376        int ret = 0;
377
378        if (!(cls = sysfs_open_class("i2c-adapter"))) {
379                if (errno && errno != ENOENT)
380                        ret = -SENSORS_ERR_PROC;
381                goto exit0;
382        }
383
384        if (!(clsdevs = sysfs_get_class_devices(cls))) {
385                if (errno && errno != ENOENT)
386                        ret = -SENSORS_ERR_PROC;
387                goto exit1;
388        }
389
390        dlist_for_each_data(clsdevs, clsdev, struct sysfs_class_device) {
391                struct sysfs_device *dev;
392                struct sysfs_attribute *attr;
393
394                /* Get the adapter name from the classdev "name" attribute
395                 * (Linux 2.6.20 and later). If it fails, fall back to
396                 * the device "name" attribute (for older kernels). */
397                if (!(attr = sysfs_get_classdev_attr(clsdev, "name"))
398                 && !((dev = sysfs_get_classdev_device(clsdev)) &&
399                      (attr = sysfs_get_device_attr(dev, "name"))))
400                        continue;
401
402                if (sscanf(clsdev->name, "i2c-%hd", &entry.bus.nr) != 1 ||
403                    entry.bus.nr == 9191) /* legacy ISA */
404                        continue;
405                entry.bus.type = SENSORS_BUS_TYPE_I2C;
406
407                /* NB: attr->value[attr->len-1] == '\n'; chop that off */
408                entry.adapter = strndup(attr->value, attr->len - 1);
409                if (!entry.adapter)
410                        sensors_fatal_error(__FUNCTION__, "out of memory");
411
412                sensors_add_proc_bus(&entry);
413        }
414
415exit1:
416        /* this frees *cls _and_ *clsdevs */
417        sysfs_close_class(cls);
418
419exit0:
420        return ret;
421}
422
423int sensors_read_sysfs_attr(const sensors_chip_name *name, int feature,
424                            double *value)
425{
426        const sensors_chip_feature *the_feature;
427        char n[NAME_MAX];
428        FILE *f;
429        const char *suffix = "";
430
431        if (!(the_feature = sensors_lookup_feature_nr(name, feature)))
432                return -SENSORS_ERR_NO_ENTRY;
433
434        /* REVISIT: this is a ugly hack */
435        if (the_feature->data.type == SENSORS_FEATURE_IN
436         || the_feature->data.type == SENSORS_FEATURE_FAN
437         || the_feature->data.type == SENSORS_FEATURE_TEMP)
438                suffix = "_input";
439
440        snprintf(n, NAME_MAX, "%s/%s%s", name->path, the_feature->data.name,
441                 suffix);
442        if ((f = fopen(n, "r"))) {
443                int res = fscanf(f, "%lf", value);
444                fclose(f);
445                if (res != 1)
446                        return -SENSORS_ERR_PROC;
447                *value /= get_type_scaling(the_feature->data.type);
448        } else
449                return -SENSORS_ERR_PROC;
450
451        return 0;
452}
453
454int sensors_write_sysfs_attr(const sensors_chip_name *name, int feature,
455                             double value)
456{
457        const sensors_chip_feature *the_feature;
458        char n[NAME_MAX];
459        FILE *f;
460        const char *suffix = "";
461
462        if (!(the_feature = sensors_lookup_feature_nr(name, feature)))
463                return -SENSORS_ERR_NO_ENTRY;
464
465        /* REVISIT: this is a ugly hack */
466        if (the_feature->data.type == SENSORS_FEATURE_IN
467         || the_feature->data.type == SENSORS_FEATURE_FAN
468         || the_feature->data.type == SENSORS_FEATURE_TEMP)
469                suffix = "_input";
470
471        snprintf(n, NAME_MAX, "%s/%s%s", name->path, the_feature->data.name,
472                 suffix);
473        if ((f = fopen(n, "w"))) {
474                value *= get_type_scaling(the_feature->data.type);
475                fprintf(f, "%d", (int) value);
476                fclose(f);
477        } else
478                return -SENSORS_ERR_PROC;
479
480        return 0;
481}
Note: See TracBrowser for help on using the browser.