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

Revision 4838, 15.2 KB (checked in by khali, 6 years ago)

Introduce a separate type for main features. Make subfeatures map to
the real main feature rather than the first subfeature.

  • 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_SUBFEATURES         8
42/* Room for all 3 types (in, fan, temp) with all their subfeatures + VID
43   + misc features */
44#define ALL_POSSIBLE_SUBFEATURES \
45                                (MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 \
46                                 + MAX_SENSORS_PER_TYPE + 1)
47
48static
49int get_type_scaling(int type)
50{
51        switch (type & 0xFF10) {
52        case SENSORS_SUBFEATURE_IN_INPUT:
53        case SENSORS_SUBFEATURE_TEMP_INPUT:
54                return 1000;
55        case SENSORS_SUBFEATURE_FAN_INPUT:
56                return 1;
57        }
58
59        switch (type) {
60        case SENSORS_SUBFEATURE_VID:
61        case SENSORS_SUBFEATURE_TEMP_OFFSET:
62                return 1000;
63        default:
64                return 1;
65        }
66}
67
68static
69char *get_feature_name(sensors_feature_type ftype, char *sfname)
70{
71        char *name, *underscore;
72
73        switch (ftype) {
74        case SENSORS_FEATURE_IN:
75        case SENSORS_FEATURE_FAN:
76        case SENSORS_FEATURE_TEMP:
77                underscore = strchr(sfname, '_');
78                name = strndup(sfname, underscore - sfname);
79                break;
80        default:
81                name = strdup(sfname);
82        }
83
84        return name;
85}
86
87/* Static mappings for use by sensors_subfeature_get_type() */
88struct subfeature_type_match
89{
90        const char *name;
91        sensors_subfeature_type type;
92};
93
94struct feature_type_match
95{
96        const char *name;
97        const struct subfeature_type_match *submatches;
98};
99
100static const struct subfeature_type_match temp_matches[] = {
101        { "input", SENSORS_SUBFEATURE_TEMP_INPUT },
102        { "max", SENSORS_SUBFEATURE_TEMP_MAX },
103        { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST },
104        { "min", SENSORS_SUBFEATURE_TEMP_MIN },
105        { "crit", SENSORS_SUBFEATURE_TEMP_CRIT },
106        { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST },
107        { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM },
108        { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM },
109        { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM },
110        { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM },
111        { "fault", SENSORS_SUBFEATURE_TEMP_FAULT },
112        { "type", SENSORS_SUBFEATURE_TEMP_TYPE },
113        { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET },
114        { NULL, 0 }
115};
116
117static const struct subfeature_type_match in_matches[] = {
118        { "input", SENSORS_SUBFEATURE_IN_INPUT },
119        { "min", SENSORS_SUBFEATURE_IN_MIN },
120        { "max", SENSORS_SUBFEATURE_IN_MAX },
121        { "alarm", SENSORS_SUBFEATURE_IN_ALARM },
122        { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM },
123        { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM },
124        { NULL, 0 }
125};
126
127static const struct subfeature_type_match fan_matches[] = {
128        { "input", SENSORS_SUBFEATURE_FAN_INPUT },
129        { "min", SENSORS_SUBFEATURE_FAN_MIN },
130        { "div", SENSORS_SUBFEATURE_FAN_DIV },
131        { "alarm", SENSORS_SUBFEATURE_FAN_ALARM },
132        { "fault", SENSORS_SUBFEATURE_FAN_FAULT },
133        { NULL, 0 }
134};
135
136static const struct subfeature_type_match cpu_matches[] = {
137        { "vid", SENSORS_SUBFEATURE_VID },
138        { NULL, 0 }
139};
140
141static struct feature_type_match matches[] = {
142        { "temp%d%c", temp_matches },
143        { "in%d%c", in_matches },
144        { "fan%d%c", fan_matches },
145        { "cpu%d%c", cpu_matches },
146};
147
148/* Return the subfeature type and channel number based on the subfeature
149   name */
150static
151sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr)
152{
153        char c;
154        int i, count;
155        const struct subfeature_type_match *submatches;
156
157        /* Special case */
158        if (!strcmp(name, "beep_enable")) {
159                *nr = 0;
160                return SENSORS_SUBFEATURE_BEEP_ENABLE;
161        }
162
163        for (i = 0; i < ARRAY_SIZE(matches); i++)
164                if ((count = sscanf(name, matches[i].name, nr, &c)))
165                        break;
166
167        if (i == ARRAY_SIZE(matches) || count != 2 || c != '_')
168                return SENSORS_SUBFEATURE_UNKNOWN;  /* no match */
169
170        submatches = matches[i].submatches;
171        name = strchr(name + 3, '_') + 1;
172        for (i = 0; submatches[i].name != NULL; i++)
173                if (!strcmp(name, submatches[i].name))
174                        return submatches[i].type;
175
176        return SENSORS_SUBFEATURE_UNKNOWN;
177}
178
179static int sensors_read_dynamic_chip(sensors_chip_features *chip,
180                                     struct sysfs_device *sysdir)
181{
182        int i, fnum = 0, sfnum = 0, prev_slot;
183        struct sysfs_attribute *attr;
184        struct dlist *attrs;
185        sensors_subfeature *all_subfeatures;
186        sensors_subfeature *dyn_subfeatures;
187        sensors_feature *dyn_features;
188        sensors_feature_type ftype;
189        sensors_subfeature_type sftype;
190
191        attrs = sysfs_get_device_attributes(sysdir);
192
193        if (attrs == NULL)
194                return -ENOENT;
195
196        /* We use a large sparse table at first to store all found
197           subfeatures, so that we can store them sorted at type and index
198           and then later create a dense sorted table. */
199        all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES,
200                                 sizeof(sensors_subfeature));
201        if (!all_subfeatures)
202                sensors_fatal_error(__FUNCTION__, "Out of memory");
203
204        dlist_for_each_data(attrs, attr, struct sysfs_attribute) {
205                char *name = attr->name;
206                int nr;
207
208                sftype = sensors_subfeature_get_type(name, &nr);
209                if (sftype == SENSORS_SUBFEATURE_UNKNOWN)
210                        continue;
211
212                /* Adjust the channel number */
213                switch (sftype & 0xFF00) {
214                        case SENSORS_SUBFEATURE_FAN_INPUT:
215                        case SENSORS_SUBFEATURE_TEMP_INPUT:
216                                if (nr)
217                                        nr--;
218                                break;
219                }
220
221                if (nr >= MAX_SENSORS_PER_TYPE) {
222                        fprintf(stderr, "libsensors error, more sensors of one"
223                                " type then MAX_SENSORS_PER_TYPE, ignoring "
224                                "subfeature: %s\n", name);
225                        continue;
226                }
227
228                /* "calculate" a place to store the subfeature in our sparse,
229                   sorted table */
230                switch (sftype) {
231                case SENSORS_SUBFEATURE_VID:
232                        i = nr + MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6;
233                        break;
234                case SENSORS_SUBFEATURE_BEEP_ENABLE:
235                        i = MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 +
236                            MAX_SENSORS_PER_TYPE;
237                        break;
238                default:
239                        i = (sftype >> 8) * MAX_SENSORS_PER_TYPE *
240                            MAX_SUBFEATURES * 2 + nr * MAX_SUBFEATURES * 2 +
241                            ((sftype & 0x10) >> 4) * MAX_SUBFEATURES +
242                            (sftype & 0x0F);
243                }
244
245                if (all_subfeatures[i].name) {
246                        fprintf(stderr, "libsensors error, trying to add dupli"
247                                "cate subfeature: %s to dynamic feature table\n",
248                                name);
249                        continue;
250                }
251
252                /* fill in the subfeature members */
253                all_subfeatures[i].type = sftype;
254                all_subfeatures[i].name = strdup(name);
255                if (!(sftype & 0x10))
256                        all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING;
257                if (attr->method & SYSFS_METHOD_SHOW)
258                        all_subfeatures[i].flags |= SENSORS_MODE_R;
259                if (attr->method & SYSFS_METHOD_STORE)
260                        all_subfeatures[i].flags |= SENSORS_MODE_W;
261
262                sfnum++;
263        }
264
265        if (!sfnum) { /* No subfeature */
266                chip->subfeature = NULL;
267                goto exit_free;
268        }
269
270        /* How many main features? */
271        prev_slot = -1;
272        for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
273                if (!all_subfeatures[i].name)
274                        continue;
275
276                if (i >= MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 ||
277                    i / (MAX_SUBFEATURES * 2) != prev_slot) {
278                        fnum++;
279                        prev_slot = i / (MAX_SUBFEATURES * 2);
280                }
281        }
282
283        dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature));
284        dyn_features = calloc(fnum, sizeof(sensors_feature));
285        if (!dyn_subfeatures || !dyn_features)
286                sensors_fatal_error(__FUNCTION__, "Out of memory");
287
288        /* Copy from the sparse array to the compact array */
289        sfnum = 0;
290        fnum = -1;
291        prev_slot = -1;
292        for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
293                if (!all_subfeatures[i].name)
294                        continue;
295
296                /* New main feature? */
297                if (i >= MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 ||
298                    i / (MAX_SUBFEATURES * 2) != prev_slot) {
299                        ftype = all_subfeatures[i].type >> 8;
300                        fnum++;
301                        prev_slot = i / (MAX_SUBFEATURES * 2);
302
303                        dyn_features[fnum].name = get_feature_name(ftype,
304                                                all_subfeatures[i].name);
305                        dyn_features[fnum].number = fnum;
306                        dyn_features[fnum].first_subfeature = sfnum;
307                        dyn_features[fnum].type = ftype;
308                }
309
310                dyn_subfeatures[sfnum] = all_subfeatures[i];
311                dyn_subfeatures[sfnum].number = sfnum;
312                /* Back to the feature */
313                dyn_subfeatures[sfnum].mapping = fnum;
314
315                sfnum++;
316        }
317
318        chip->subfeature = dyn_subfeatures;
319        chip->subfeature_count = sfnum;
320        chip->feature = dyn_features;
321        chip->feature_count = ++fnum;
322
323exit_free:
324        free(all_subfeatures);
325        return 0;
326}
327
328/* returns !0 if sysfs filesystem was found, 0 otherwise */
329int sensors_init_sysfs(void)
330{
331        struct stat statbuf;
332
333        /* libsysfs will return success even if sysfs is not mounted,
334           so we have to double-check */
335        if (sysfs_get_mnt_path(sensors_sysfs_mount, NAME_MAX)
336         || stat(sensors_sysfs_mount, &statbuf) < 0
337         || statbuf.st_nlink <= 2)      /* Empty directory */
338                return 0;
339
340        return 1;
341}
342
343/* returns: 0 if successful, !0 otherwise */
344static int sensors_read_one_sysfs_chip(struct sysfs_device *dev)
345{
346        int domain, bus, slot, fn;
347        int err = -SENSORS_ERR_PARSE;
348        struct sysfs_attribute *attr, *bus_attr;
349        char bus_path[SYSFS_PATH_MAX];
350        sensors_chip_features entry;
351
352        /* ignore any device without name attribute */
353        if (!(attr = sysfs_get_device_attr(dev, "name")))
354                return 0;
355
356        /* NB: attr->value[attr->len-1] == '\n'; chop that off */
357        entry.chip.prefix = strndup(attr->value, attr->len - 1);
358        if (!entry.chip.prefix)
359                sensors_fatal_error(__FUNCTION__, "out of memory");
360
361        entry.chip.path = strdup(dev->path);
362        if (!entry.chip.path)
363                sensors_fatal_error(__FUNCTION__, "out of memory");
364
365        if (sscanf(dev->name, "%hd-%x", &entry.chip.bus.nr, &entry.chip.addr) == 2) {
366                /* find out if legacy ISA or not */
367                if (entry.chip.bus.nr == 9191) {
368                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
369                        entry.chip.bus.nr = 0;
370                } else {
371                        entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
372                        snprintf(bus_path, sizeof(bus_path),
373                                "%s/class/i2c-adapter/i2c-%d/device/name",
374                                sensors_sysfs_mount, entry.chip.bus.nr);
375
376                        if ((bus_attr = sysfs_open_attribute(bus_path))) {
377                                if (sysfs_read_attribute(bus_attr)) {
378                                        sysfs_close_attribute(bus_attr);
379                                        goto exit_free;
380                                }
381
382                                if (bus_attr->value
383                                 && !strncmp(bus_attr->value, "ISA ", 4)) {
384                                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
385                                        entry.chip.bus.nr = 0;
386                                }
387
388                                sysfs_close_attribute(bus_attr);
389                        }
390                }
391        } else if (sscanf(dev->name, "spi%hd.%d", &entry.chip.bus.nr,
392                          &entry.chip.addr) == 2) {
393                /* SPI */
394                entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
395        } else if (sscanf(dev->name, "%*[a-z0-9_].%d", &entry.chip.addr) == 1) {
396                /* must be new ISA (platform driver) */
397                entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
398                entry.chip.bus.nr = 0;
399        } else if (sscanf(dev->name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
400                /* PCI */
401                entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
402                entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
403                entry.chip.bus.nr = 0;
404        } else
405                goto exit_free;
406
407        if (sensors_read_dynamic_chip(&entry, dev) < 0)
408                goto exit_free;
409        if (!entry.subfeature) { /* No subfeature, discard chip */
410                err = 0;
411                goto exit_free;
412        }
413        sensors_add_proc_chips(&entry);
414
415        return 0;
416
417exit_free:
418        free(entry.chip.prefix);
419        free(entry.chip.path);
420        return err;
421}
422
423/* returns 0 if successful, !0 otherwise */
424static int sensors_read_sysfs_chips_compat(void)
425{
426        struct sysfs_bus *bus;
427        struct dlist *devs;
428        struct sysfs_device *dev;
429        int ret = 0;
430
431        if (!(bus = sysfs_open_bus("i2c"))) {
432                if (errno && errno != ENOENT)
433                        ret = -SENSORS_ERR_PROC;
434                goto exit0;
435        }
436
437        if (!(devs = sysfs_get_bus_devices(bus))) {
438                if (errno && errno != ENOENT)
439                        ret = -SENSORS_ERR_PROC;
440                goto exit1;
441        }
442
443        dlist_for_each_data(devs, dev, struct sysfs_device)
444                if ((ret = sensors_read_one_sysfs_chip(dev)))
445                        goto exit1;
446
447exit1:
448        /* this frees bus and devs */
449        sysfs_close_bus(bus);
450
451exit0:
452        return ret;
453}
454
455/* returns 0 if successful, !0 otherwise */
456int sensors_read_sysfs_chips(void)
457{
458        struct sysfs_class *cls;
459        struct dlist *clsdevs;
460        struct sysfs_class_device *clsdev;
461        int ret = 0;
462
463        if (!(cls = sysfs_open_class("hwmon"))) {
464                /* compatibility function for kernel 2.6.n where n <= 13 */
465                return sensors_read_sysfs_chips_compat();
466        }
467
468        if (!(clsdevs = sysfs_get_class_devices(cls))) {
469                if (errno && errno != ENOENT)
470                        ret = -SENSORS_ERR_PROC;
471                goto exit;
472        }
473
474        dlist_for_each_data(clsdevs, clsdev, struct sysfs_class_device) {
475                struct sysfs_device *dev;
476                if (!(dev = sysfs_get_classdev_device(clsdev))) {
477                        ret = -SENSORS_ERR_PROC;
478                        goto exit;
479                }
480                if ((ret = sensors_read_one_sysfs_chip(dev)))
481                        goto exit;
482        }
483
484exit:
485        /* this frees cls and clsdevs */
486        sysfs_close_class(cls);
487        return ret;
488}
489
490/* returns 0 if successful, !0 otherwise */
491int sensors_read_sysfs_bus(void)
492{
493        struct sysfs_class *cls;
494        struct dlist *clsdevs;
495        struct sysfs_class_device *clsdev;
496        sensors_bus entry;
497        int ret = 0;
498
499        if (!(cls = sysfs_open_class("i2c-adapter"))) {
500                if (errno && errno != ENOENT)
501                        ret = -SENSORS_ERR_PROC;
502                goto exit0;
503        }
504
505        if (!(clsdevs = sysfs_get_class_devices(cls))) {
506                if (errno && errno != ENOENT)
507                        ret = -SENSORS_ERR_PROC;
508                goto exit1;
509        }
510
511        dlist_for_each_data(clsdevs, clsdev, struct sysfs_class_device) {
512                struct sysfs_device *dev;
513                struct sysfs_attribute *attr;
514
515                /* Get the adapter name from the classdev "name" attribute
516                 * (Linux 2.6.20 and later). If it fails, fall back to
517                 * the device "name" attribute (for older kernels). */
518                if (!(attr = sysfs_get_classdev_attr(clsdev, "name"))
519                 && !((dev = sysfs_get_classdev_device(clsdev)) &&
520                      (attr = sysfs_get_device_attr(dev, "name"))))
521                        continue;
522
523                if (sscanf(clsdev->name, "i2c-%hd", &entry.bus.nr) != 1 ||
524                    entry.bus.nr == 9191) /* legacy ISA */
525                        continue;
526                entry.bus.type = SENSORS_BUS_TYPE_I2C;
527
528                /* NB: attr->value[attr->len-1] == '\n'; chop that off */
529                entry.adapter = strndup(attr->value, attr->len - 1);
530                if (!entry.adapter)
531                        sensors_fatal_error(__FUNCTION__, "out of memory");
532
533                sensors_add_proc_bus(&entry);
534        }
535
536exit1:
537        /* this frees *cls _and_ *clsdevs */
538        sysfs_close_class(cls);
539
540exit0:
541        return ret;
542}
543
544int sensors_read_sysfs_attr(const sensors_chip_name *name, int subfeat_nr,
545                            double *value)
546{
547        const sensors_subfeature *subfeature;
548        char n[NAME_MAX];
549        FILE *f;
550
551        if (!(subfeature = sensors_lookup_subfeature_nr(name, subfeat_nr)))
552                return -SENSORS_ERR_NO_ENTRY;
553
554        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
555        if ((f = fopen(n, "r"))) {
556                int res = fscanf(f, "%lf", value);
557                fclose(f);
558                if (res != 1)
559                        return -SENSORS_ERR_PROC;
560                *value /= get_type_scaling(subfeature->type);
561        } else
562                return -SENSORS_ERR_PROC;
563
564        return 0;
565}
566
567int sensors_write_sysfs_attr(const sensors_chip_name *name, int subfeat_nr,
568                             double value)
569{
570        const sensors_subfeature *subfeature;
571        char n[NAME_MAX];
572        FILE *f;
573
574        if (!(subfeature = sensors_lookup_subfeature_nr(name, subfeat_nr)))
575                return -SENSORS_ERR_NO_ENTRY;
576
577        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
578        if ((f = fopen(n, "w"))) {
579                value *= get_type_scaling(subfeature->type);
580                fprintf(f, "%d", (int) value);
581                fclose(f);
582        } else
583                return -SENSORS_ERR_PROC;
584
585        return 0;
586}
Note: See TracBrowser for help on using the browser.