/* * 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 #include "igt_sysfs.h" /** * SECTION:igt_sysfs * @short_description: Support code for sysfs features * @title: sysfs * @include: igt.h * * This library provides helpers to access sysfs features. Right now it only * provides basic support for like igt_sysfs_open(). */ 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) * @idx: optional pointer to store the card index of the opened device * * 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 device, int *idx) { char path[80]; struct stat st; if (device != -1 && (fstat(device, &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 (device != -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_open_parameters: * @device: fd of the device (or -1 to default to Intel) * * This opens the module parameters directory (under sysfs) corresponding * to the device for use with igt_sysfs_set() and igt_sysfs_get(). * * Returns: * The directory fd, or -1 on failure. */ int igt_sysfs_open_parameters(int device) { int dir, params; dir = igt_sysfs_open(device, ¶ms); if (dir < 0) return -1; params = -1; //params = openat(dir, "device/driver/module/parameters", O_RDONLY); close(dir); if (params < 0) { /* builtin? */ drm_version_t version; char name[32] = ""; char path[128]; memset(&version, 0, sizeof(version)); version.name_len = sizeof(name); version.name = name; ioctl(device, DRM_IOCTL_VERSION, &version); sprintf(path, "/sys/module/%s/parameters", name); params = open(path, O_RDONLY); } return params; } /** * 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; } /** * igt_sysfs_scanf: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * @fmt: scanf format string * @...: Additional paramaters to store the scaned input values * * scanf() wrapper for sysfs. * * Returns: * Number of values successfully scanned (which can be 0), EOF on errors or * premature end of file. */ int igt_sysfs_scanf(int dir, const char *attr, const char *fmt, ...) { FILE *file; int fd; int ret = -1; fd = openat(dir, attr, O_RDONLY); if (fd < 0) return -1; file = fdopen(fd, "r"); if (file) { va_list ap; va_start(ap, fmt); ret = vfscanf(file, fmt, ap); va_end(ap); fclose(file); } close(fd); return ret; } /** * igt_sysfs_printf: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * @fmt: printf format string * @...: Additional paramaters to store the scaned input values * * printf() wrapper for sysfs. * * Returns: * Number of characters written, negative value on error. */ int igt_sysfs_printf(int dir, const char *attr, const char *fmt, ...) { FILE *file; int fd; int ret = -1; fd = openat(dir, attr, O_WRONLY); if (fd < 0) return -1; file = fdopen(fd, "w"); if (file) { va_list ap; va_start(ap, fmt); ret = vfprintf(file, fmt, ap); va_end(ap); fclose(file); } close(fd); return ret; } /** * igt_sysfs_get_u32: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * * Convenience wrapper to read a unsigned 32bit integer from a sysfs file. * * Returns: * The value read. */ uint32_t igt_sysfs_get_u32(int dir, const char *attr) { uint32_t result; if (igt_sysfs_scanf(dir, attr, "%u", &result) != 1) return 0; return result; } /** * igt_sysfs_set_u32: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * @value: value to set * * Convenience wrapper to write a unsigned 32bit integer to a sysfs file. * * Returns: * True if successfully written */ bool igt_sysfs_set_u32(int dir, const char *attr, uint32_t value) { return igt_sysfs_printf(dir, attr, "%u", value) == 1; } /** * igt_sysfs_get_boolean: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * * Convenience wrapper to read a boolean sysfs file. * * Returns: * The value read. */ bool igt_sysfs_get_boolean(int dir, const char *attr) { int result; if (igt_sysfs_scanf(dir, attr, "%d", &result) != 1) return false; return result; } /** * igt_sysfs_set_boolean: * @dir: directory for the device from igt_sysfs_open() * @attr: name of the sysfs node to open * @value: value to set * * Convenience wrapper to write a boolean sysfs file. * * Returns: * The value read. */ bool igt_sysfs_set_boolean(int dir, const char *attr, bool value) { return igt_sysfs_printf(dir, attr, "%d", value) == 1; }