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

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

Patch from Aurelien Jarno:

I have just noticed that the FSF address is the old one in all files
except COPYING. Please find a patch below to fix that.

  • 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., 51 Franklin Street, Fifth Floor, Boston,
19    MA 02110-1301 USA.
20*/
21
22/* this define needed for strndup() */
23#define _GNU_SOURCE
24
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <unistd.h>
28#include <string.h>
29#include <stdlib.h>
30#include <limits.h>
31#include <errno.h>
32#include <dirent.h>
33#include "data.h"
34#include "error.h"
35#include "access.h"
36#include "general.h"
37#include "sysfs.h"
38
39
40/****************************************************************************/
41
42#define ATTR_MAX        128
43
44/*
45 * Read an attribute from sysfs
46 * Returns a pointer to a freshly allocated string; free it yourself.
47 * If the file doesn't exist or can't be read, NULL is returned.
48 */
49static char *sysfs_read_attr(const char *device, const char *attr)
50{
51        char path[NAME_MAX];
52        char buf[ATTR_MAX], *p;
53        FILE *f;
54
55        snprintf(path, NAME_MAX, "%s/%s", device, attr);
56
57        if (!(f = fopen(path, "r")))
58                return NULL;
59        p = fgets(buf, ATTR_MAX, f);
60        fclose(f);
61        if (!p)
62                return NULL;
63
64        /* Last byte is a '\n'; chop that off */
65        p = strndup(buf, strlen(buf) - 1);
66        if (!p)
67                sensors_fatal_error(__func__, "Out of memory");
68        return p;
69}
70
71/*
72 * Call an arbitrary function for each class device of the given class
73 * Returns 0 on success (all calls returned 0), a positive errno for
74 * local errors, or a negative error value if any call fails.
75 */
76static int sysfs_foreach_classdev(const char *class_name,
77                                   int (*func)(const char *, const char *))
78{
79        char path[NAME_MAX];
80        int path_off, ret;
81        DIR *dir;
82        struct dirent *ent;
83
84        path_off = snprintf(path, NAME_MAX, "%s/class/%s",
85                            sensors_sysfs_mount, class_name);
86        if (!(dir = opendir(path)))
87                return errno;
88
89        ret = 0;
90        while (!ret && (ent = readdir(dir))) {
91                if (ent->d_name[0] == '.')      /* skip hidden entries */
92                        continue;
93
94                snprintf(path + path_off, NAME_MAX - path_off, "/%s",
95                         ent->d_name);
96                ret = func(path, ent->d_name);
97        }
98
99        closedir(dir);
100        return ret;
101}
102
103/*
104 * Call an arbitrary function for each device of the given bus type
105 * Returns 0 on success (all calls returned 0), a positive errno for
106 * local errors, or a negative error value if any call fails.
107 */
108static int sysfs_foreach_busdev(const char *bus_type,
109                                 int (*func)(const char *, const char *))
110{
111        char path[NAME_MAX];
112        int path_off, ret;
113        DIR *dir;
114        struct dirent *ent;
115
116        path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices",
117                            sensors_sysfs_mount, bus_type);
118        if (!(dir = opendir(path)))
119                return errno;
120
121        ret = 0;
122        while (!ret && (ent = readdir(dir))) {
123                if (ent->d_name[0] == '.')      /* skip hidden entries */
124                        continue;
125
126                snprintf(path + path_off, NAME_MAX - path_off, "/%s",
127                         ent->d_name);
128                ret = func(path, ent->d_name);
129        }
130
131        closedir(dir);
132        return ret;
133}
134
135/****************************************************************************/
136
137char sensors_sysfs_mount[NAME_MAX];
138
139#define MAX_SENSORS_PER_TYPE    20
140#define MAX_SUBFEATURES         8
141/* Room for all 3 types (in, fan, temp) with all their subfeatures + VID
142   + misc features */
143#define ALL_POSSIBLE_SUBFEATURES \
144                                (MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 \
145                                 + MAX_SENSORS_PER_TYPE + 1)
146
147static
148int get_type_scaling(sensors_subfeature_type type)
149{
150        switch (type & 0xFF80) {
151        case SENSORS_SUBFEATURE_IN_INPUT:
152        case SENSORS_SUBFEATURE_TEMP_INPUT:
153                return 1000;
154        case SENSORS_SUBFEATURE_FAN_INPUT:
155                return 1;
156        }
157
158        switch (type) {
159        case SENSORS_SUBFEATURE_VID:
160        case SENSORS_SUBFEATURE_TEMP_OFFSET:
161                return 1000;
162        default:
163                return 1;
164        }
165}
166
167static
168char *get_feature_name(sensors_feature_type ftype, char *sfname)
169{
170        char *name, *underscore;
171
172        switch (ftype) {
173        case SENSORS_FEATURE_IN:
174        case SENSORS_FEATURE_FAN:
175        case SENSORS_FEATURE_TEMP:
176                underscore = strchr(sfname, '_');
177                name = strndup(sfname, underscore - sfname);
178                break;
179        default:
180                name = strdup(sfname);
181        }
182
183        return name;
184}
185
186/* Static mappings for use by sensors_subfeature_get_type() */
187struct subfeature_type_match
188{
189        const char *name;
190        sensors_subfeature_type type;
191};
192
193struct feature_type_match
194{
195        const char *name;
196        const struct subfeature_type_match *submatches;
197};
198
199static const struct subfeature_type_match temp_matches[] = {
200        { "input", SENSORS_SUBFEATURE_TEMP_INPUT },
201        { "max", SENSORS_SUBFEATURE_TEMP_MAX },
202        { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST },
203        { "min", SENSORS_SUBFEATURE_TEMP_MIN },
204        { "crit", SENSORS_SUBFEATURE_TEMP_CRIT },
205        { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST },
206        { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM },
207        { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM },
208        { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM },
209        { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM },
210        { "fault", SENSORS_SUBFEATURE_TEMP_FAULT },
211        { "type", SENSORS_SUBFEATURE_TEMP_TYPE },
212        { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET },
213        { NULL, 0 }
214};
215
216static const struct subfeature_type_match in_matches[] = {
217        { "input", SENSORS_SUBFEATURE_IN_INPUT },
218        { "min", SENSORS_SUBFEATURE_IN_MIN },
219        { "max", SENSORS_SUBFEATURE_IN_MAX },
220        { "alarm", SENSORS_SUBFEATURE_IN_ALARM },
221        { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM },
222        { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM },
223        { NULL, 0 }
224};
225
226static const struct subfeature_type_match fan_matches[] = {
227        { "input", SENSORS_SUBFEATURE_FAN_INPUT },
228        { "min", SENSORS_SUBFEATURE_FAN_MIN },
229        { "div", SENSORS_SUBFEATURE_FAN_DIV },
230        { "alarm", SENSORS_SUBFEATURE_FAN_ALARM },
231        { "fault", SENSORS_SUBFEATURE_FAN_FAULT },
232        { NULL, 0 }
233};
234
235static const struct subfeature_type_match cpu_matches[] = {
236        { "vid", SENSORS_SUBFEATURE_VID },
237        { NULL, 0 }
238};
239
240static struct feature_type_match matches[] = {
241        { "temp%d%c", temp_matches },
242        { "in%d%c", in_matches },
243        { "fan%d%c", fan_matches },
244        { "cpu%d%c", cpu_matches },
245};
246
247/* Return the subfeature type and channel number based on the subfeature
248   name */
249static
250sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr)
251{
252        char c;
253        int i, count;
254        const struct subfeature_type_match *submatches;
255
256        /* Special case */
257        if (!strcmp(name, "beep_enable")) {
258                *nr = 0;
259                return SENSORS_SUBFEATURE_BEEP_ENABLE;
260        }
261
262        for (i = 0; i < ARRAY_SIZE(matches); i++)
263                if ((count = sscanf(name, matches[i].name, nr, &c)))
264                        break;
265
266        if (i == ARRAY_SIZE(matches) || count != 2 || c != '_')
267                return SENSORS_SUBFEATURE_UNKNOWN;  /* no match */
268
269        submatches = matches[i].submatches;
270        name = strchr(name + 3, '_') + 1;
271        for (i = 0; submatches[i].name != NULL; i++)
272                if (!strcmp(name, submatches[i].name))
273                        return submatches[i].type;
274
275        return SENSORS_SUBFEATURE_UNKNOWN;
276}
277
278static int sensors_get_attr_mode(const char *device, const char *attr)
279{
280        char path[NAME_MAX];
281        struct stat st;
282        int mode = 0;
283
284        snprintf(path, NAME_MAX, "%s/%s", device, attr);
285        if (!stat(path, &st)) {
286                if (st.st_mode & S_IRUSR)
287                        mode |= SENSORS_MODE_R;
288                if (st.st_mode & S_IWUSR)
289                        mode |= SENSORS_MODE_W;
290        }
291        return mode;
292}
293
294static int sensors_read_dynamic_chip(sensors_chip_features *chip,
295                                     const char *dev_path)
296{
297        int i, fnum = 0, sfnum = 0, prev_slot;
298        DIR *dir;
299        struct dirent *ent;
300        sensors_subfeature *all_subfeatures;
301        sensors_subfeature *dyn_subfeatures;
302        sensors_feature *dyn_features;
303        sensors_feature_type ftype;
304        sensors_subfeature_type sftype;
305
306        if (!(dir = opendir(dev_path)))
307                return -errno;
308
309        /* We use a large sparse table at first to store all found
310           subfeatures, so that we can store them sorted at type and index
311           and then later create a dense sorted table. */
312        all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES,
313                                 sizeof(sensors_subfeature));
314        if (!all_subfeatures)
315                sensors_fatal_error(__func__, "Out of memory");
316
317        while ((ent = readdir(dir))) {
318                char *name = ent->d_name;
319                int nr;
320
321                if (ent->d_name[0] == '.')
322                        continue;
323
324                sftype = sensors_subfeature_get_type(name, &nr);
325                if (sftype == SENSORS_SUBFEATURE_UNKNOWN)
326                        continue;
327
328                /* Adjust the channel number */
329                switch (sftype & 0xFF00) {
330                        case SENSORS_SUBFEATURE_FAN_INPUT:
331                        case SENSORS_SUBFEATURE_TEMP_INPUT:
332                                nr--;
333                                break;
334                }
335
336                if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) {
337                        /* More sensors of one type than MAX_SENSORS_PER_TYPE,
338                           we have to ignore it */
339#ifdef DEBUG
340                        sensors_fatal_error(__func__,
341                                            "Increase MAX_SENSORS_PER_TYPE!");
342#endif
343                        continue;
344                }
345
346                /* "calculate" a place to store the subfeature in our sparse,
347                   sorted table */
348                switch (sftype) {
349                case SENSORS_SUBFEATURE_VID:
350                        i = nr + MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6;
351                        break;
352                case SENSORS_SUBFEATURE_BEEP_ENABLE:
353                        i = MAX_SENSORS_PER_TYPE * MAX_SUBFEATURES * 6 +
354                            MAX_SENSORS_PER_TYPE;
355                        break;
356                default:
357                        i = (sftype >> 8) * MAX_SENSORS_PER_TYPE *
358                            MAX_SUBFEATURES * 2 + nr * MAX_SUBFEATURES * 2 +
359                            ((sftype & 0x80) >> 7) * MAX_SUBFEATURES +
360                            (sftype & 0x7F);
361                }
362
363                if (all_subfeatures[i].name) {
364#ifdef DEBUG
365                        sensors_fatal_error(__func__, "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(__func__, "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(__func__, "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, "pci")) &&
529            sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
530                /* PCI */
531                entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
532                entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
533                entry.chip.bus.nr = 0;
534        } else
535        if ((!subsys || !strcmp(subsys, "platform"))) {
536                /* must be new ISA (platform driver) */
537                if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
538                        entry.chip.addr = 0;
539                entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
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.