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

Revision 5092, 17.6 KB (checked in by khali, 5 years ago)

Get the device type from the subsystem symlink, rather than
guessing it from how the device identifier looks like. For kernels
<= 2.6.17, we use the bus symlink instead. For kernels <= 2.6.11,
neither symlink exist so we fallback to the old type guessing
approach. It doesn't really matter as all hwmon devices were
i2c devices back then.

  • 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: 0 if successful, !0 otherwise */
458static int sensors_read_one_sysfs_chip(const char *dev_path, const char *dev_name)
459{
460        int domain, bus, slot, fn;
461        int err = -SENSORS_ERR_KERNEL;
462        char *bus_attr;
463        char bus_path[NAME_MAX];
464        char linkpath[NAME_MAX];
465        char subsys_path[NAME_MAX], *subsys;
466        int sub_len;
467        sensors_chip_features entry;
468
469        /* ignore any device without name attribute */
470        if (!(entry.chip.prefix = sysfs_read_attr(dev_path, "name")))
471                return 0;
472
473        entry.chip.path = strdup(dev_path);
474        if (!entry.chip.path)
475                sensors_fatal_error(__FUNCTION__, "out of memory");
476
477        /* Find bus type */
478        snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path);
479        sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
480        if (sub_len < 0 && errno == ENOENT) {
481                /* Fallback to "bus" link for kernels <= 2.6.17 */
482                snprintf(linkpath, NAME_MAX, "%s/bus", dev_path);
483                sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
484        }
485        if (sub_len < 0) {
486                /* Older kernels (<= 2.6.11) have neither the subsystem
487                   symlink nor the bus symlink */
488                if (errno == ENOENT)
489                        subsys = NULL;
490                else
491                        goto exit_free;
492        } else {
493                subsys_path[sub_len] = '\0';
494                subsys = strrchr(subsys_path, '/') + 1;
495        }
496
497        if ((!subsys || !strcmp(subsys, "i2c")) &&
498            sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr,
499                   &entry.chip.addr) == 2) {
500                /* find out if legacy ISA or not */
501                if (entry.chip.bus.nr == 9191) {
502                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
503                        entry.chip.bus.nr = 0;
504                } else {
505                        entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
506                        snprintf(bus_path, sizeof(bus_path),
507                                "%s/class/i2c-adapter/i2c-%d/device",
508                                sensors_sysfs_mount, entry.chip.bus.nr);
509
510                        if ((bus_attr = sysfs_read_attr(bus_path, "name"))) {
511                                if (!strncmp(bus_attr, "ISA ", 4)) {
512                                        entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
513                                        entry.chip.bus.nr = 0;
514                                }
515
516                                free(bus_attr);
517                        }
518                }
519        } else
520        if ((!subsys || !strcmp(subsys, "spi")) &&
521            sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr,
522                   &entry.chip.addr) == 2) {
523                /* SPI */
524                entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
525        } else
526        if ((!subsys || !strcmp(subsys, "platform"))) {
527                /* must be new ISA (platform driver) */
528                if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
529                        entry.chip.addr = 0;
530                entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
531                entry.chip.bus.nr = 0;
532        } else
533        if ((!subsys || !strcmp(subsys, "pci")) &&
534            sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
535                /* PCI */
536                entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
537                entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
538                entry.chip.bus.nr = 0;
539        } else {
540                /* Ignore unknown device */
541                err = 0;
542                goto exit_free;
543        }
544
545        if (sensors_read_dynamic_chip(&entry, dev_path) < 0)
546                goto exit_free;
547        if (!entry.subfeature) { /* No subfeature, discard chip */
548                err = 0;
549                goto exit_free;
550        }
551        sensors_add_proc_chips(&entry);
552
553        return 0;
554
555exit_free:
556        free(entry.chip.prefix);
557        free(entry.chip.path);
558        return err;
559}
560
561/* returns 0 if successful, !0 otherwise */
562static int sensors_read_sysfs_chips_compat(void)
563{
564        int ret;
565
566        ret = sysfs_foreach_busdev("i2c", sensors_read_one_sysfs_chip);
567        if (ret && ret != ENOENT)
568                return -SENSORS_ERR_KERNEL;
569
570        return 0;
571}
572
573static int sensors_add_hwmon_device(const char *path, const char *classdev)
574{
575        char linkpath[NAME_MAX];
576        char device[NAME_MAX];
577        int dev_len;
578        (void)classdev; /* hide warning */
579
580        snprintf(linkpath, NAME_MAX, "%s/device", path);
581        dev_len = readlink(linkpath, device, NAME_MAX - 1);
582        if (dev_len < 0)
583                return -SENSORS_ERR_KERNEL;
584        device[dev_len] = '\0';
585
586        return sensors_read_one_sysfs_chip(linkpath, strrchr(device, '/') + 1);
587}
588
589/* returns 0 if successful, !0 otherwise */
590int sensors_read_sysfs_chips(void)
591{
592        int ret;
593
594        ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device);
595        if (ret == ENOENT) {
596                /* compatibility function for kernel 2.6.n where n <= 13 */
597                return sensors_read_sysfs_chips_compat();
598        }
599
600        if (ret > 0)
601                ret = -SENSORS_ERR_KERNEL;
602        return ret;
603}
604
605/* returns 0 if successful, !0 otherwise */
606static int sensors_add_i2c_bus(const char *path, const char *classdev)
607{
608        sensors_bus entry;
609
610        if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 ||
611            entry.bus.nr == 9191) /* legacy ISA */
612                return 0;
613        entry.bus.type = SENSORS_BUS_TYPE_I2C;
614
615        /* Get the adapter name from the classdev "name" attribute
616         * (Linux 2.6.20 and later). If it fails, fall back to
617         * the device "name" attribute (for older kernels). */
618        entry.adapter = sysfs_read_attr(path, "name");
619        if (!entry.adapter)
620                entry.adapter = sysfs_read_attr(path, "device/name");
621        if (entry.adapter)
622                sensors_add_proc_bus(&entry);
623
624        return 0;
625}
626
627/* returns 0 if successful, !0 otherwise */
628int sensors_read_sysfs_bus(void)
629{
630        int ret;
631
632        ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus);
633        if (ret && ret != ENOENT)
634                return -SENSORS_ERR_KERNEL;
635
636        return 0;
637}
638
639int sensors_read_sysfs_attr(const sensors_chip_name *name,
640                            const sensors_subfeature *subfeature,
641                            double *value)
642{
643        char n[NAME_MAX];
644        FILE *f;
645
646        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
647        if ((f = fopen(n, "r"))) {
648                int res, err = 0;
649
650                errno = 0;
651                res = fscanf(f, "%lf", value);
652                if (res == EOF && errno == EIO)
653                        err = -SENSORS_ERR_IO;
654                else if (res != 1)
655                        err = -SENSORS_ERR_ACCESS_R;
656                res = fclose(f);
657                if (err)
658                        return err;
659
660                if (res == EOF) {
661                        if (errno == EIO)
662                                return -SENSORS_ERR_IO;
663                        else 
664                                return -SENSORS_ERR_ACCESS_R;
665                }
666                *value /= get_type_scaling(subfeature->type);
667        } else
668                return -SENSORS_ERR_KERNEL;
669
670        return 0;
671}
672
673int sensors_write_sysfs_attr(const sensors_chip_name *name,
674                             const sensors_subfeature *subfeature,
675                             double value)
676{
677        char n[NAME_MAX];
678        FILE *f;
679
680        snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
681        if ((f = fopen(n, "w"))) {
682                int res, err = 0;
683
684                value *= get_type_scaling(subfeature->type);
685                res = fprintf(f, "%d", (int) value);
686                if (res == -EIO)
687                        err = -SENSORS_ERR_IO;
688                else if (res < 0)
689                        err = -SENSORS_ERR_ACCESS_W;
690                res = fclose(f);
691                if (err)
692                        return err;
693
694                if (res == EOF) {
695                        if (errno == EIO)
696                                return -SENSORS_ERR_IO;
697                        else 
698                                return -SENSORS_ERR_ACCESS_W;
699                }
700        } else
701                return -SENSORS_ERR_KERNEL;
702
703        return 0;
704}
Note: See TracBrowser for help on using the browser.