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

Revision 5093, 18.2 KB (checked in by khali, 6 years ago)

Add support for drivers that place the hwmon attributes in the hwmon
class device directory rather than directly in the device directory.
The latter is what all drivers do at the moment, but in the long run
the former is preferred as it prevents attribute name collisions.

  • 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 <dirent.h>
32#include "data.h"
33#include "error.h"
34#include "access.h"
35#include "general.h"
36#include "sysfs.h"
37
38
39/****************************************************************************/
40
41#define ATTR_MAX        128
42
43/*
44 * Read an attribute from sysfs
45 * Returns a pointer to a freshly allocated string; free it yourself.
46 * If the file doesn't exist or can't be read, NULL is returned.
47 */
48static char *sysfs_read_attr(const char *device, const char *attr)
49{
50        char path[NAME_MAX];
51        char buf[ATTR_MAX], *p;
52        FILE *f;
53
54        snprintf(path, NAME_MAX, "%s/%s", device, attr);
55
56        if (!(f = fopen(path, "r")))
57                return NULL;
58        p = fgets(buf, ATTR_MAX, f);
59        fclose(f);
60        if (!p)
61                return NULL;
62
63        /* Last byte is a '\n'; chop that off */
64        p = strndup(buf, strlen(buf) - 1);
65        if (!p)
66                sensors_fatal_error(__FUNCTION__, "out of memory");
67        return p;
68}
69
70/*
71 * Call an arbitrary function for each class device of the given class
72 * Returns 0 on success (all calls returned 0), a positive errno for
73 * local errors, or a negative error value if any call fails.
74 */
75static int sysfs_foreach_classdev(const char *class_name,
76                                   int (*func)(const char *, const char *))
77{
78        char path[NAME_MAX];
79        int path_off, ret;
80        DIR *dir;
81        struct dirent *ent;
82
83        path_off = snprintf(path, NAME_MAX, "%s/class/%s",
84                            sensors_sysfs_mount, class_name);
85        if (!(dir = opendir(path)))
86                return errno;
87
88        ret = 0;
89        while (!ret && (ent = readdir(dir))) {
90                if (ent->d_name[0] == '.')      /* skip hidden entries */
91                        continue;
92
93                snprintf(path + path_off, NAME_MAX - path_off, "/%s",
94                         ent->d_name);
95                ret = func(path, ent->d_name);
96        }
97
98        closedir(dir);
99        return ret;
100}
101
102/*
103 * Call an arbitrary function for each device of the given bus type
104 * Returns 0 on success (all calls returned 0), a positive errno for
105 * local errors, or a negative error value if any call fails.
106 */
107static int sysfs_foreach_busdev(const char *bus_type,
108                                 int (*func)(const char *, const char *))
109{
110        char path[NAME_MAX];
111        int path_off, ret;
112        DIR *dir;
113        struct dirent *ent;
114
115        path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices",
116                            sensors_sysfs_mount, bus_type);
117        if (!(dir = opendir(path)))
118                return errno;
119
120        ret = 0;
121        while (!ret && (ent = readdir(dir))) {
122                if (ent->d_name[0] == '.')      /* skip hidden entries */
123                        continue;
124
125                snprintf(path + path_off, NAME_MAX - path_off, "/%s",
126                         ent->d_name);
127                ret = func(path, ent->d_name);
128        }
129
130        closedir(dir);
131        return ret;
132}
133
134/****************************************************************************/
135
136char sensors_sysfs_mount[NAME_MAX];
137
138#define MAX_SENSORS_PER_TYPE    20
139#define MAX_SUBFEATURES         8
140/* Room for all 3 types (in, fan, temp) with all their subfeatures + VID
141   + misc features */
142#define ALL_POSSIBLE_SUBFEATURES \
143                                (MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 \
144                                 + MAX_SENSORS_PER_TYPE + 1)
145
146static
147int get_type_scaling(sensors_subfeature_type type)
148{
149        switch (type & 0xFF80) {
150        case SENSORS_SUBFEATURE_IN_INPUT:
151        case SENSORS_SUBFEATURE_TEMP_INPUT:
152                return 1000;
153        case SENSORS_SUBFEATURE_FAN_INPUT:
154                return 1;
155        }
156
157        switch (type) {
158        case SENSORS_SUBFEATURE_VID:
159        case SENSORS_SUBFEATURE_TEMP_OFFSET:
160                return 1000;
161        default:
162                return 1;
163        }
164}
165
166static
167char *get_feature_name(sensors_feature_type ftype, char *sfname)
168{
169        char *name, *underscore;
170
171        switch (ftype) {
172        case SENSORS_FEATURE_IN:
173        case SENSORS_FEATURE_FAN:
174        case SENSORS_FEATURE_TEMP:
175                underscore = strchr(sfname, '_');
176                name = strndup(sfname, underscore - sfname);
177                break;
178        default:
179                name = strdup(sfname);
180        }
181
182        return name;
183}
184
185/* Static mappings for use by sensors_subfeature_get_type() */
186struct subfeature_type_match
187{
188        const char *name;
189        sensors_subfeature_type type;
190};
191
192struct feature_type_match
193{
194        const char *name;
195        const struct subfeature_type_match *submatches;
196};
197
198static const struct subfeature_type_match temp_matches[] = {
199        { "input", SENSORS_SUBFEATURE_TEMP_INPUT },
200        { "max", SENSORS_SUBFEATURE_TEMP_MAX },
201        { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST },
202        { "min", SENSORS_SUBFEATURE_TEMP_MIN },
203        { "crit", SENSORS_SUBFEATURE_TEMP_CRIT },
204        { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST },
205        { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM },
206        { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM },
207        { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM },
208        { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM },
209        { "fault", SENSORS_SUBFEATURE_TEMP_FAULT },
210        { "type", SENSORS_SUBFEATURE_TEMP_TYPE },
211        { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET },
212        { NULL, 0 }
213};
214
215static const struct subfeature_type_match in_matches[] = {
216        { "input", SENSORS_SUBFEATURE_IN_INPUT },
217        { "min", SENSORS_SUBFEATURE_IN_MIN },
218        { "max", SENSORS_SUBFEATURE_IN_MAX },
219        { "alarm", SENSORS_SUBFEATURE_IN_ALARM },
220        { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM },
221        { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM },
222        { NULL, 0 }
223};
224
225static const struct subfeature_type_match fan_matches[] = {
226        { "input", SENSORS_SUBFEATURE_FAN_INPUT },
227        { "min", SENSORS_SUBFEATURE_FAN_MIN },
228        { "div", SENSORS_SUBFEATURE_FAN_DIV },
229        { "alarm", SENSORS_SUBFEATURE_FAN_ALARM },
230        { "fault", SENSORS_SUBFEATURE_FAN_FAULT },
231        { NULL, 0 }
232};
233
234static const struct subfeature_type_match cpu_matches[] = {
235        { "vid", SENSORS_SUBFEATURE_VID },
236        { NULL, 0 }
237};
238
239static struct feature_type_match matches[] = {
240        { "temp%d%c", temp_matches },
241        { "in%d%c", in_matches },
242        { "fan%d%c", fan_matches },
243        { "cpu%d%c", cpu_matches },
244};
245
246/* Return the subfeature type and channel number based on the subfeature
247   name */
248static
249sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr)
250{
251        char c;
252        int i, count;
253        const struct subfeature_type_match *submatches;
254
255        /* Special case */
256        if (!strcmp(name, "beep_enable")) {
257                *nr = 0;
258                return SENSORS_SUBFEATURE_BEEP_ENABLE;
259        }
260
261        for (i = 0; i < ARRAY_SIZE(matches); i++)
262                if ((count = sscanf(name, matches[i].name, nr, &c)))
263                        break;
264
265        if (i == ARRAY_SIZE(matches) || count != 2 || c != '_')
266                return SENSORS_SUBFEATURE_UNKNOWN;  /* no match */
267
268        submatches = matches[i].submatches;
269        name = strchr(name + 3, '_') + 1;
270        for (i = 0; submatches[i].name != NULL; i++)
271                if (!strcmp(name, submatches[i].name))
272                        return submatches[i].type;
273
274        return SENSORS_SUBFEATURE_UNKNOWN;
275}
276
277static int sensors_get_attr_mode(const char *device, const char *attr)
278{
279        char path[NAME_MAX];
280        struct stat st;
281        int mode = 0;
282
283        snprintf(path, NAME_MAX, "%s/%s", device, attr);
284        if (!stat(path, &st)) {
285                if (st.st_mode & S_IRUSR)
286                        mode |= SENSORS_MODE_R;
287                if (st.st_mode & S_IWUSR)
288                        mode |= SENSORS_MODE_W;
289        }
290        return mode;
291}
292
293static int sensors_read_dynamic_chip(sensors_chip_features *chip,
294                                     const char *dev_path)
295{
296        int i, fnum = 0, sfnum = 0, prev_slot;
297        DIR *dir;
298        struct dirent *ent;
299        sensors_subfeature *all_subfeatures;
300        sensors_subfeature *dyn_subfeatures;
301        sensors_feature *dyn_features;
302        sensors_feature_type ftype;
303        sensors_subfeature_type sftype;
304
305        if (!(dir = opendir(dev_path)))
306                return -errno;
307
308        /* We use a large sparse table at first to store all found
309           subfeatures, so that we can store them sorted at type and index
310           and then later create a dense sorted table. */
311        all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES,
312                                 sizeof(sensors_subfeature));
313        if (!all_subfeatures)
314                sensors_fatal_error(__FUNCTION__, "Out of memory");
315
316        while ((ent = readdir(dir))) {
317                char *name = ent->d_name;
318                int nr;
319
320                if (ent->d_name[0] == '.')
321                        continue;
322
323                sftype = sensors_subfeature_get_type(name, &nr);
324                if (sftype == SENSORS_SUBFEATURE_UNKNOWN)
325                        continue;
326
327                /* Adjust the channel number */
328                switch (sftype & 0xFF00) {
329                        case SENSORS_SUBFEATURE_FAN_INPUT:
330                        case SENSORS_SUBFEATURE_TEMP_INPUT:
331                                nr--;
332                                break;
333                }
334
335                if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) {
336                        /* More sensors of one type than MAX_SENSORS_PER_TYPE,
337                           we have to ignore it */
338#ifdef DEBUG
339                        sensors_fatal_error(__FUNCTION__,
340                                            "Increase MAX_SENSORS_PER_TYPE!");
341#endif
342                        continue;
343                }
344
345                /* "calculate" a place to store the subfeature in our sparse,
346                   sorted table */
347                switch (sftype) {
348                case SENSORS_SUBFEATURE_VID:
349                        i = nr + MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6;
350                        break;
351                case SENSORS_SUBFEATURE_BEEP_ENABLE:
352                        i = MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 +
353                            MAX_SENSORS_PER_TYPE;
354                        break;
355                default:
356                        i = (sftype >> 8) * MAX_SENSORS_PER_TYPE *
357                            MAX_SUBFEATURES * 2 + nr * MAX_SUBFEATURES * 2 +
358                            ((sftype & 0x80) >> 7) * MAX_SUBFEATURES +
359                            (sftype & 0x7F);
360                }
361
362                if (all_subfeatures[i].name) {
363#ifdef DEBUG
364                        sensors_fatal_error(__FUNCTION__,
365                                            "Duplicate subfeature");
366#endif
367                        continue;
368                }
369
370                /* fill in the subfeature members */
371                all_subfeatures[i].type = sftype;
372                all_subfeatures[i].name = strdup(name);
373                if (!(sftype & 0x80))
374                        all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING;
375                all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name);
376
377                sfnum++;
378        }
379        closedir(dir);
380
381        if (!sfnum) { /* No subfeature */
382                chip->subfeature = NULL;
383                goto exit_free;
384        }
385
386        /* How many main features? */
387        prev_slot = -1;
388        for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
389                if (!all_subfeatures[i].name)
390                        continue;
391
392                if (i >= MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 ||
393                    i / (MAX_SUBFEATURES * 2) != prev_slot) {
394                        fnum++;
395                        prev_slot = i / (MAX_SUBFEATURES * 2);
396                }
397        }
398
399        dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature));
400        dyn_features = calloc(fnum, sizeof(sensors_feature));
401        if (!dyn_subfeatures || !dyn_features)
402                sensors_fatal_error(__FUNCTION__, "Out of memory");
403
404        /* Copy from the sparse array to the compact array */
405        sfnum = 0;
406        fnum = -1;
407        prev_slot = -1;
408        for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
409                if (!all_subfeatures[i].name)
410                        continue;
411
412                /* New main feature? */
413                if (i >= MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 ||
414                    i / (MAX_SUBFEATURES * 2) != prev_slot) {
415                        ftype = all_subfeatures[i].type >> 8;
416                        fnum++;
417                        prev_slot = i / (MAX_SUBFEATURES * 2);
418
419                        dyn_features[fnum].name = get_feature_name(ftype,
420                                                all_subfeatures[i].name);
421                        dyn_features[fnum].number = fnum;
422                        dyn_features[fnum].first_subfeature = sfnum;
423                        dyn_features[fnum].type = ftype;
424                }
425
426                dyn_subfeatures[sfnum] = all_subfeatures[i];
427                dyn_subfeatures[sfnum].number = sfnum;
428                /* Back to the feature */
429                dyn_subfeatures[sfnum].mapping = fnum;
430
431                sfnum++;
432        }
433
434        chip->subfeature = dyn_subfeatures;
435        chip->subfeature_count = sfnum;
436        chip->feature = dyn_features;
437        chip->feature_count = ++fnum;
438
439exit_free:
440        free(all_subfeatures);
441        return 0;
442}
443
444/* returns !0 if sysfs filesystem was found, 0 otherwise */
445int sensors_init_sysfs(void)
446{
447        struct stat statbuf;
448
449        snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys");
450        if (stat(sensors_sysfs_mount, &statbuf) < 0
451         || statbuf.st_nlink <= 2)      /* Empty directory */
452                return 0;
453
454        return 1;
455}
456
457/* returns: number of devices added (0 or 1) if successful, <0 otherwise */
458static int sensors_read_one_sysfs_chip(const char *dev_path,
459                                       const char *dev_name,
460                                       const char *hwmon_path)
461{
462        int domain, bus, slot, fn;
463        int err = -SENSORS_ERR_KERNEL;
464        char *bus_attr;
465        char bus_path[NAME_MAX];
466        char linkpath[NAME_MAX];
467        char subsys_path[NAME_MAX], *subsys;
468        int sub_len;
469        sensors_chip_features entry;
470
471        /* ignore any device without name attribute */
472        if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name")))
473                return 0;
474
475        entry.chip.path = strdup(hwmon_path);
476        if (!entry.chip.path)
477                sensors_fatal_error(__FUNCTION__, "out of memory");
478
479        /* Find bus type */
480        snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path);
481        sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
482        if (sub_len < 0 && errno == ENOENT) {
483                /* Fallback to "bus" link for kernels <= 2.6.17 */
484                snprintf(linkpath, NAME_MAX, "%s/bus", dev_path);
485                sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
486        }
487        if (sub_len < 0) {
488                /* Older kernels (<= 2.6.11) have neither the subsystem
489                   symlink nor the bus symlink */
490                if (errno == ENOENT)
491                        subsys = NULL;
492                else
493                        goto exit_free;
494        } else {
495                subsys_path[sub_len] = '\0';
496                subsys = strrchr(subsys_path, '/') + 1;
497        }
498
499        if ((!subsys || !strcmp(subsys, "i2c")) &&
500            sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr,
501                   &entry.chip.addr) == 2) {
502                /* find out if legacy ISA or not */
503                if (entry.chip.bus.nr == 9191) {
504                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
505                        entry.chip.bus.nr = 0;
506                } else {
507                        entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
508                        snprintf(bus_path, sizeof(bus_path),
509                                "%s/class/i2c-adapter/i2c-%d/device",
510                                sensors_sysfs_mount, entry.chip.bus.nr);
511
512                        if ((bus_attr = sysfs_read_attr(bus_path, "name"))) {
513                                if (!strncmp(bus_attr, "ISA ", 4)) {
514                                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
515                                        entry.chip.bus.nr = 0;
516                                }
517
518                                free(bus_attr);
519                        }
520                }
521        } else
522        if ((!subsys || !strcmp(subsys, "spi")) &&
523            sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr,
524                   &entry.chip.addr) == 2) {
525                /* SPI */
526                entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
527        } else
528        if ((!subsys || !strcmp(subsys, "platform"))) {
529                /* must be new ISA (platform driver) */
530                if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
531                        entry.chip.addr = 0;
532                entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
533                entry.chip.bus.nr = 0;
534        } else
535        if ((!subsys || !strcmp(subsys, "pci")) &&
536            sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
537                /* PCI */
538                entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
539                entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
540                entry.chip.bus.nr = 0;
541        } else {
542                /* Ignore unknown device */
543                err = 0;
544                goto exit_free;
545        }
546
547        if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0)
548                goto exit_free;
549        if (!entry.subfeature) { /* No subfeature, discard chip */
550                err = 0;
551                goto exit_free;
552        }
553        sensors_add_proc_chips(&entry);
554
555        return 1;
556
557exit_free:
558        free(entry.chip.prefix);
559        free(entry.chip.path);
560        return err;
561}
562
563static int sensors_add_hwmon_device_compat(const char *path,
564                                           const char *dev_name)
565{
566        int err;
567
568        err = sensors_read_one_sysfs_chip(path, dev_name, path);
569        if (err < 0)
570                return err;
571        return 0;
572}
573
574/* returns 0 if successful, !0 otherwise */
575static int sensors_read_sysfs_chips_compat(void)
576{
577        int ret;
578
579        ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat);
580        if (ret && ret != ENOENT)
581                return -SENSORS_ERR_KERNEL;
582
583        return 0;
584}
585
586static int sensors_add_hwmon_device(const char *path, const char *classdev)
587{
588        char linkpath[NAME_MAX];
589        char device[NAME_MAX], *device_p;
590        int dev_len, err;
591        (void)classdev; /* hide warning */
592
593        snprintf(linkpath, NAME_MAX, "%s/device", path);
594        dev_len = readlink(linkpath, device, NAME_MAX - 1);
595        if (dev_len < 0)
596                return -SENSORS_ERR_KERNEL;
597        device[dev_len] = '\0';
598        device_p = strrchr(device, '/') + 1;
599
600        /* The attributes we want might be those of the hwmon class device,
601           or those of the device itself. */
602        err = sensors_read_one_sysfs_chip(linkpath, device_p, path);
603        if (err == 0)
604                err = sensors_read_one_sysfs_chip(linkpath, device_p, linkpath);
605        if (err < 0)
606                return err;
607        return 0;
608}
609
610/* returns 0 if successful, !0 otherwise */
611int sensors_read_sysfs_chips(void)
612{
613        int ret;
614
615        ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device);
616        if (ret == ENOENT) {
617                /* compatibility function for kernel 2.6.n where n <= 13 */
618                return sensors_read_sysfs_chips_compat();
619        }
620
621        if (ret > 0)
622                ret = -SENSORS_ERR_KERNEL;
623        return ret;
624}
625
626/* returns 0 if successful, !0 otherwise */
627static int sensors_add_i2c_bus(const char *path, const char *classdev)
628{
629        sensors_bus entry;
630
631        if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 ||
632            entry.bus.nr == 9191) /* legacy ISA */
633                return 0;
634        entry.bus.type = SENSORS_BUS_TYPE_I2C;
635
636        /* Get the adapter name from the classdev "name" attribute
637         * (Linux 2.6.20 and later). If it fails, fall back to
638         * the device "name" attribute (for older kernels). */
639        entry.adapter = sysfs_read_attr(path, "name");
640        if (!entry.adapter)
641                entry.adapter = sysfs_read_attr(path, "device/name");
642        if (entry.adapter)
643                sensors_add_proc_bus(&entry);
644
645        return 0;
646}
647
648/* returns 0 if successful, !0 otherwise */
649int sensors_read_sysfs_bus(void)
650{
651        int ret;
652
653        ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus);
654        if (ret && ret != ENOENT)
655                return -SENSORS_ERR_KERNEL;
656
657        return 0;
658}
659
660int sensors_read_sysfs_attr(const sensors_chip_name *name,
661                            const sensors_subfeature *subfeature,
662                            double *value)
663{
664        char n[NAME_MAX];
665        FILE *f;
666
667        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
668        if ((f = fopen(n, "r"))) {
669                int res, err = 0;
670
671                errno = 0;
672                res = fscanf(f, "%lf", value);
673                if (res == EOF && errno == EIO)
674                        err = -SENSORS_ERR_IO;
675                else if (res != 1)
676                        err = -SENSORS_ERR_ACCESS_R;
677                res = fclose(f);
678                if (err)
679                        return err;
680
681                if (res == EOF) {
682                        if (errno == EIO)
683                                return -SENSORS_ERR_IO;
684                        else 
685                                return -SENSORS_ERR_ACCESS_R;
686                }
687                *value /= get_type_scaling(subfeature->type);
688        } else
689                return -SENSORS_ERR_KERNEL;
690
691        return 0;
692}
693
694int sensors_write_sysfs_attr(const sensors_chip_name *name,
695                             const sensors_subfeature *subfeature,
696                             double value)
697{
698        char n[NAME_MAX];
699        FILE *f;
700
701        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
702        if ((f = fopen(n, "w"))) {
703                int res, err = 0;
704
705                value *= get_type_scaling(subfeature->type);
706                res = fprintf(f, "%d", (int) value);
707                if (res == -EIO)
708                        err = -SENSORS_ERR_IO;
709                else if (res < 0)
710                        err = -SENSORS_ERR_ACCESS_W;
711                res = fclose(f);
712                if (err)
713                        return err;
714
715                if (res == EOF) {
716                        if (errno == EIO)
717                                return -SENSORS_ERR_IO;
718                        else 
719                                return -SENSORS_ERR_ACCESS_W;
720                }
721        } else
722                return -SENSORS_ERR_KERNEL;
723
724        return 0;
725}
Note: See TracBrowser for help on using the browser.