| /* |
| * 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 <inttypes.h> |
| #include <sys/stat.h> |
| #include <sys/mount.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <i915_drm.h> |
| #include <dirent.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #include "igt_core.h" |
| #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; |
| if (total == len) |
| return total; |
| } 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; |
| if (total == len) |
| return total; |
| } while (1); |
| } |
| |
| /** |
| * igt_sysfs_path: |
| * @device: fd of the device (or -1 to default to Intel) |
| * @path: buffer to fill with the sysfs path to the device |
| * @pathlen: length of @path buffer |
| * @idx: optional pointer to store the card index of the opened device |
| * |
| * This finds the sysfs directory corresponding to @device. |
| * |
| * Returns: |
| * The directory path, or NULL on failure. |
| */ |
| char *igt_sysfs_path(int device, char *path, int pathlen, int *idx) |
| { |
| struct stat st; |
| |
| if (device != -1 && (fstat(device, &st) || !S_ISCHR(st.st_mode))) |
| return NULL; |
| |
| for (int n = 0; n < 16; n++) { |
| int len = snprintf(path, pathlen, "/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 path; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * 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]; |
| |
| if (!igt_sysfs_path(device, path, sizeof(path), idx)) |
| return -1; |
| |
| return open(path, O_RDONLY); |
| } |
| |
| /** |
| * igt_sysfs_set_parameters: |
| * @device: fd of the device (or -1 to default to Intel) |
| * @parameter: the name of the parameter to set |
| * @fmt: printf-esque format string |
| * |
| * Returns true on success |
| */ |
| bool igt_sysfs_set_parameter(int device, |
| const char *parameter, |
| const char *fmt, ...) |
| { |
| va_list ap; |
| int dir; |
| int ret; |
| |
| dir = igt_sysfs_open_parameters(device); |
| if (dir < 0) |
| return false; |
| |
| va_start(ap, fmt); |
| ret = igt_sysfs_vprintf(dir, parameter, fmt, ap); |
| va_end(ap); |
| |
| close(dir); |
| |
| return ret > 0; |
| } |
| |
| /** |
| * 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[PATH_MAX]; |
| |
| 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_write: |
| * @dir: directory for the device from igt_sysfs_open() |
| * @attr: name of the sysfs node to open |
| * @data: the block to write from |
| * @len: the length to write |
| * |
| * This writes @len bytes from @data to the sysfs file. |
| * |
| * Returns: |
| * The number of bytes written, or -1 on error. |
| */ |
| int igt_sysfs_write(int dir, const char *attr, const void *data, int len) |
| { |
| int fd; |
| |
| fd = openat(dir, attr, O_WRONLY); |
| if (fd < 0) |
| return false; |
| |
| len = writeN(fd, data, len); |
| close(fd); |
| |
| return len; |
| } |
| |
| /** |
| * igt_sysfs_read: |
| * @dir: directory for the device from igt_sysfs_open() |
| * @attr: name of the sysfs node to open |
| * @data: the block to read into |
| * @len: the maximum length to read |
| * |
| * This reads @len bytes from the sysfs file to @data |
| * |
| * Returns: |
| * The length read, -1 on failure. |
| */ |
| int igt_sysfs_read(int dir, const char *attr, void *data, int len) |
| { |
| int fd; |
| |
| fd = openat(dir, attr, O_RDONLY); |
| if (fd < 0) |
| return false; |
| |
| len = readN(fd, data, len); |
| close(fd); |
| |
| return len; |
| } |
| |
| /** |
| * 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 len = strlen(value); |
| return igt_sysfs_write(dir, attr, value, len) == len; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| int igt_sysfs_vprintf(int dir, const char *attr, const char *fmt, va_list ap) |
| { |
| FILE *file; |
| int fd; |
| int ret = -1; |
| |
| fd = openat(dir, attr, O_WRONLY); |
| if (fd < 0) |
| return -1; |
| |
| file = fdopen(fd, "w"); |
| if (file) { |
| ret = vfprintf(file, fmt, 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, ...) |
| { |
| va_list ap; |
| int ret; |
| |
| va_start(ap, fmt); |
| ret = igt_sysfs_vprintf(dir, attr, fmt, ap); |
| va_end(ap); |
| |
| 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; |
| } |
| |
| /** |
| * kick_fbcon: |
| * @enable: boolean value |
| * |
| * This functions enables/disables the text console running on top of the |
| * framebuffer device. |
| */ |
| void kick_fbcon(bool enable) |
| { |
| const char *path = "/sys/class/vtconsole"; |
| DIR *dir; |
| struct dirent *de; |
| |
| dir = opendir(path); |
| if (!dir) |
| return; |
| |
| while ((de = readdir(dir))) { |
| char buf[PATH_MAX]; |
| int fd, len; |
| |
| if (strncmp(de->d_name, "vtcon", 5)) |
| continue; |
| |
| sprintf(buf, "%s/%s/name", path, de->d_name); |
| fd = open(buf, O_RDONLY); |
| if (fd < 0) |
| continue; |
| |
| buf[sizeof(buf) - 1] = '\0'; |
| len = read(fd, buf, sizeof(buf) - 1); |
| close(fd); |
| if (len >= 0) |
| buf[len] = '\0'; |
| |
| if (!strstr(buf, enable ? "dummy device" : |
| "frame buffer device")) |
| continue; |
| |
| sprintf(buf, "%s/%s/bind", path, de->d_name); |
| fd = open(buf, O_WRONLY); |
| if (fd != -1) { |
| igt_ignore_warn(write(fd, "0\n", 2)); |
| close(fd); |
| } |
| } |
| closedir(dir); |
| } |
| |
| /** |
| * kick_snd_hda_intel: |
| * |
| * This functions unbinds the snd_hda_intel driver so the module cand be |
| * unloaded. |
| * |
| */ |
| void kick_snd_hda_intel(void) |
| { |
| DIR *dir; |
| struct dirent *snd_hda; |
| int fd; size_t len; |
| |
| const char *dpath = "/sys/bus/pci/drivers/snd_hda_intel"; |
| const char *path = "/sys/bus/pci/drivers/snd_hda_intel/unbind"; |
| const char *devid = "0000:"; |
| |
| fd = open(path, O_WRONLY); |
| if (fd < 0) { |
| return; |
| } |
| |
| dir = opendir(dpath); |
| if (!dir) |
| goto out; |
| |
| len = strlen(devid); |
| while ((snd_hda = readdir(dir))) { |
| struct stat st; |
| char fpath[PATH_MAX]; |
| |
| if (*snd_hda->d_name == '.') |
| continue; |
| |
| snprintf(fpath, sizeof(fpath), "%s/%s", dpath, snd_hda->d_name); |
| if (lstat(fpath, &st)) |
| continue; |
| |
| if (!S_ISLNK(st.st_mode)) |
| continue; |
| |
| if (!strncmp(devid, snd_hda->d_name, len)) { |
| igt_ignore_warn(write(fd, snd_hda->d_name, |
| strlen(snd_hda->d_name))); |
| } |
| } |
| |
| closedir(dir); |
| out: |
| close(fd); |
| } |