/* * Copyright © 2016 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #include #include #include "igt_sysfs.h" static int readN(int fd, char *buf, int len) { int total = 0; do { int ret = read(fd, buf + total, len - total); if (ret < 0 && (errno == EINTR || errno == EAGAIN)) continue; if (ret <= 0) return total ?: ret; total += ret; } while (1); } static int writeN(int fd, const char *buf, int len) { int total = 0; do { int ret = write(fd, buf + total, len - total); if (ret < 0 && (errno == EINTR || errno == EAGAIN)) continue; if (ret <= 0) return total ?: ret; total += ret; } while (1); } /** * igt_sysfs_open: * @device: fd of the device (or -1 to default to Intel) * * This opens the sysfs directory corresponding to device for use * with igt_sysfs_set() and igt_sysfs_get(). * * Returns: * The directory fd, or -1 on failure. */ int igt_sysfs_open(int fd, int *idx) { char path[80]; struct stat st; if (fd != -1 && (fstat(fd, &st) || !S_ISCHR(st.st_mode))) return -1; for (int n = 0; n < 16; n++) { int len = sprintf(path, "/sys/class/drm/card%d", n); if (fd != -1) { FILE *file; int ret, maj, min; sprintf(path + len, "/dev"); file = fopen(path, "r"); if (!file) continue; ret = fscanf(file, "%d:%d", &maj, &min); fclose(file); if (ret != 2 || major(st.st_rdev) != maj || minor(st.st_rdev) != min) continue; } else { /* Bleh. Search for intel */ sprintf(path + len, "/error"); if (stat(path, &st)) continue; } path[len] = '\0'; if (idx) *idx = n; return open(path, O_RDONLY); } return -1; } /** * igt_sysfs_set: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * @value: the string to write * * This writes the value to the sysfs file. * * Returns: * True on success, false on failure. */ bool igt_sysfs_set(int dir, const char *attr, const char *value) { int fd, len, ret; fd = openat(dir, attr, O_WRONLY); if (fd < 0) return false; len = strlen(value); ret = writeN(fd, value, len); close(fd); return len == ret; } /** * igt_sysfs_get: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * * This reads the value from the sysfs file. * * Returns: * A nul-terminated string, must be freed by caller after use, or NULL * on failure. */ char *igt_sysfs_get(int dir, const char *attr) { char *buf; int len, offset, rem; int ret, fd; fd = openat(dir, attr, O_RDONLY); if (fd < 0) return NULL; offset = 0; len = 64; rem = len - offset - 1; buf = malloc(len); if (!buf) goto out; while ((ret = readN(fd, buf + offset, rem)) == rem) { char *newbuf; newbuf = realloc(buf, 2*len); if (!newbuf) break; buf = newbuf; len *= 2; offset += ret; rem = len - offset - 1; } if (ret != -1) offset += ret; buf[offset] = '\0'; while (offset > 0 && buf[offset-1] == '\n') buf[--offset] = '\0'; out: close(fd); return buf; }