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

Revision 5145, 18.2 KB (checked in by jwrdegoede, 6 years ago)

The platform case
acts as a fallback solution so it must be last in the list.

  • 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-2008 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(__func__, "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(__func__, "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(__func__,
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(__func__, "Duplicate subfeature");
365#endif
366                        continue;
367                }
368
369                /* fill in the subfeature members */
370                all_subfeatures[i].type = sftype;
371                all_subfeatures[i].name = strdup(name);
372                if (!(sftype & 0x80))
373                        all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING;
374                all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name);
375
376                sfnum++;
377        }
378        closedir(dir);
379
380        if (!sfnum) { /* No subfeature */
381                chip->subfeature = NULL;
382                goto exit_free;
383        }
384
385        /* How many main features? */
386        prev_slot = -1;
387        for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
388                if (!all_subfeatures[i].name)
389                        continue;
390
391                if (i >= MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 ||
392                    i / (MAX_SUBFEATURES * 2) != prev_slot) {
393                        fnum++;
394                        prev_slot = i / (MAX_SUBFEATURES * 2);
395                }
396        }
397
398        dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature));
399        dyn_features = calloc(fnum, sizeof(sensors_feature));
400        if (!dyn_subfeatures || !dyn_features)
401                sensors_fatal_error(__func__, "Out of memory");
402
403        /* Copy from the sparse array to the compact array */
404        sfnum = 0;
405        fnum = -1;
406        prev_slot = -1;
407        for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
408                if (!all_subfeatures[i].name)
409                        continue;
410
411                /* New main feature? */
412                if (i >= MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 ||
413                    i / (MAX_SUBFEATURES * 2) != prev_slot) {
414                        ftype = all_subfeatures[i].type >> 8;
415                        fnum++;
416                        prev_slot = i / (MAX_SUBFEATURES * 2);
417
418                        dyn_features[fnum].name = get_feature_name(ftype,
419                                                all_subfeatures[i].name);
420                        dyn_features[fnum].number = fnum;
421                        dyn_features[fnum].first_subfeature = sfnum;
422                        dyn_features[fnum].type = ftype;
423                }
424
425                dyn_subfeatures[sfnum] = all_subfeatures[i];
426                dyn_subfeatures[sfnum].number = sfnum;
427                /* Back to the feature */
428                dyn_subfeatures[sfnum].mapping = fnum;
429
430                sfnum++;
431        }
432
433        chip->subfeature = dyn_subfeatures;
434        chip->subfeature_count = sfnum;
435        chip->feature = dyn_features;
436        chip->feature_count = ++fnum;
437
438exit_free:
439        free(all_subfeatures);
440        return 0;
441}
442
443/* returns !0 if sysfs filesystem was found, 0 otherwise */
444int sensors_init_sysfs(void)
445{
446        struct stat statbuf;
447
448        snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys");
449        if (stat(sensors_sysfs_mount, &statbuf) < 0
450         || statbuf.st_nlink <= 2)      /* Empty directory */
451                return 0;
452
453        return 1;
454}
455
456/* returns: number of devices added (0 or 1) if successful, <0 otherwise */
457static int sensors_read_one_sysfs_chip(const char *dev_path,
458                                       const char *dev_name,
459                                       const char *hwmon_path)
460{
461        int domain, bus, slot, fn;
462        int err = -SENSORS_ERR_KERNEL;
463        char *bus_attr;
464        char bus_path[NAME_MAX];
465        char linkpath[NAME_MAX];
466        char subsys_path[NAME_MAX], *subsys;
467        int sub_len;
468        sensors_chip_features entry;
469
470        /* ignore any device without name attribute */
471        if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name")))
472                return 0;
473
474        entry.chip.path = strdup(hwmon_path);
475        if (!entry.chip.path)
476                sensors_fatal_error(__func__, "Out of memory");
477
478        /* Find bus type */
479        snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path);
480        sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
481        if (sub_len < 0 && errno == ENOENT) {
482                /* Fallback to "bus" link for kernels <= 2.6.17 */
483                snprintf(linkpath, NAME_MAX, "%s/bus", dev_path);
484                sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
485        }
486        if (sub_len < 0) {
487                /* Older kernels (<= 2.6.11) have neither the subsystem
488                   symlink nor the bus symlink */
489                if (errno == ENOENT)
490                        subsys = NULL;
491                else
492                        goto exit_free;
493        } else {
494                subsys_path[sub_len] = '\0';
495                subsys = strrchr(subsys_path, '/') + 1;
496        }
497
498        if ((!subsys || !strcmp(subsys, "i2c")) &&
499            sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr,
500                   &entry.chip.addr) == 2) {
501                /* find out if legacy ISA or not */
502                if (entry.chip.bus.nr == 9191) {
503                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
504                        entry.chip.bus.nr = 0;
505                } else {
506                        entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
507                        snprintf(bus_path, sizeof(bus_path),
508                                "%s/class/i2c-adapter/i2c-%d/device",
509                                sensors_sysfs_mount, entry.chip.bus.nr);
510
511                        if ((bus_attr = sysfs_read_attr(bus_path, "name"))) {
512                                if (!strncmp(bus_attr, "ISA ", 4)) {
513                                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
514                                        entry.chip.bus.nr = 0;
515                                }
516
517                                free(bus_attr);
518                        }
519                }
520        } else
521        if ((!subsys || !strcmp(subsys, "spi")) &&
522            sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr,
523                   &entry.chip.addr) == 2) {
524                /* SPI */
525                entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
526        } else
527        if ((!subsys || !strcmp(subsys, "pci")) &&
528            sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
529                /* PCI */
530                entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
531                entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
532                entry.chip.bus.nr = 0;
533        } else
534        if ((!subsys || !strcmp(subsys, "platform"))) {
535                /* must be new ISA (platform driver) */
536                if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
537                        entry.chip.addr = 0;
538                entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
539                entry.chip.bus.nr = 0;
540        } else {
541                /* Ignore unknown device */
542                err = 0;
543                goto exit_free;
544        }
545
546        if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0)
547                goto exit_free;
548        if (!entry.subfeature) { /* No subfeature, discard chip */
549                err = 0;
550                goto exit_free;
551        }
552        sensors_add_proc_chips(&entry);
553
554        return 1;
555
556exit_free:
557        free(entry.chip.prefix);
558        free(entry.chip.path);
559        return err;
560}
561
562static int sensors_add_hwmon_device_compat(const char *path,
563                                           const char *dev_name)
564{
565        int err;
566
567        err = sensors_read_one_sysfs_chip(path, dev_name, path);
568        if (err < 0)
569                return err;
570        return 0;
571}
572
573/* returns 0 if successful, !0 otherwise */
574static int sensors_read_sysfs_chips_compat(void)
575{
576        int ret;
577
578        ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat);
579        if (ret && ret != ENOENT)
580                return -SENSORS_ERR_KERNEL;
581
582        return 0;
583}
584
585static int sensors_add_hwmon_device(const char *path, const char *classdev)
586{
587        char linkpath[NAME_MAX];
588        char device[NAME_MAX], *device_p;
589        int dev_len, err;
590        (void)classdev; /* hide warning */
591
592        snprintf(linkpath, NAME_MAX, "%s/device", path);
593        dev_len = readlink(linkpath, device, NAME_MAX - 1);
594        if (dev_len < 0)
595                return -SENSORS_ERR_KERNEL;
596        device[dev_len] = '\0';
597        device_p = strrchr(device, '/') + 1;
598
599        /* The attributes we want might be those of the hwmon class device,
600           or those of the device itself. */
601        err = sensors_read_one_sysfs_chip(linkpath, device_p, path);
602        if (err == 0)
603                err = sensors_read_one_sysfs_chip(linkpath, device_p, linkpath);
604        if (err < 0)
605                return err;
606        return 0;
607}
608
609/* returns 0 if successful, !0 otherwise */
610int sensors_read_sysfs_chips(void)
611{
612        int ret;
613
614        ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device);
615        if (ret == ENOENT) {
616                /* compatibility function for kernel 2.6.n where n <= 13 */
617                return sensors_read_sysfs_chips_compat();
618        }
619
620        if (ret > 0)
621                ret = -SENSORS_ERR_KERNEL;
622        return ret;
623}
624
625/* returns 0 if successful, !0 otherwise */
626static int sensors_add_i2c_bus(const char *path, const char *classdev)
627{
628        sensors_bus entry;
629
630        if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 ||
631            entry.bus.nr == 9191) /* legacy ISA */
632                return 0;
633        entry.bus.type = SENSORS_BUS_TYPE_I2C;
634
635        /* Get the adapter name from the classdev "name" attribute
636         * (Linux 2.6.20 and later). If it fails, fall back to
637         * the device "name" attribute (for older kernels). */
638        entry.adapter = sysfs_read_attr(path, "name");
639        if (!entry.adapter)
640                entry.adapter = sysfs_read_attr(path, "device/name");
641        if (entry.adapter)
642                sensors_add_proc_bus(&entry);
643
644        return 0;
645}
646
647/* returns 0 if successful, !0 otherwise */
648int sensors_read_sysfs_bus(void)
649{
650        int ret;
651
652        ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus);
653        if (ret && ret != ENOENT)
654                return -SENSORS_ERR_KERNEL;
655
656        return 0;
657}
658
659int sensors_read_sysfs_attr(const sensors_chip_name *name,
660                            const sensors_subfeature *subfeature,
661                            double *value)
662{
663        char n[NAME_MAX];
664        FILE *f;
665
666        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
667        if ((f = fopen(n, "r"))) {
668                int res, err = 0;
669
670                errno = 0;
671                res = fscanf(f, "%lf", value);
672                if (res == EOF && errno == EIO)
673                        err = -SENSORS_ERR_IO;
674                else if (res != 1)
675                        err = -SENSORS_ERR_ACCESS_R;
676                res = fclose(f);
677                if (err)
678                        return err;
679
680                if (res == EOF) {
681                        if (errno == EIO)
682                                return -SENSORS_ERR_IO;
683                        else 
684                                return -SENSORS_ERR_ACCESS_R;
685                }
686                *value /= get_type_scaling(subfeature->type);
687        } else
688                return -SENSORS_ERR_KERNEL;
689
690        return 0;
691}
692
693int sensors_write_sysfs_attr(const sensors_chip_name *name,
694                             const sensors_subfeature *subfeature,
695                             double value)
696{
697        char n[NAME_MAX];
698        FILE *f;
699
700        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
701        if ((f = fopen(n, "w"))) {
702                int res, err = 0;
703
704                value *= get_type_scaling(subfeature->type);
705                res = fprintf(f, "%d", (int) value);
706                if (res == -EIO)
707                        err = -SENSORS_ERR_IO;
708                else if (res < 0)
709                        err = -SENSORS_ERR_ACCESS_W;
710                res = fclose(f);
711                if (err)
712                        return err;
713
714                if (res == EOF) {
715                        if (errno == EIO)
716                                return -SENSORS_ERR_IO;
717                        else 
718                                return -SENSORS_ERR_ACCESS_W;
719                }
720        } else
721                return -SENSORS_ERR_KERNEL;
722
723        return 0;
724}
Note: See TracBrowser for help on using the browser.