// SPDX-License-Identifier: GPL-2.0-only /* * ec_access.c * * Copyright (C) 2010 SUSE Linux Products GmbH * Author: * Thomas Renninger <trenn@suse.de> */ #include <fcntl.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <libgen.h> #include <unistd.h> #include <getopt.h> #include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #define EC_SPACE_SIZE 256 #define SYSFS_PATH "/sys/kernel/debug/ec/ec0/io" /* TBD/Enhancements: - Provide param for accessing different ECs (not supported by kernel yet) */ static int read_mode = -1; static int sleep_time; static int write_byte_offset = -1; static int read_byte_offset = -1; static uint8_t write_value = -1; void usage(char progname[], int exit_status) { printf("Usage:\n"); printf("1) %s -r [-s sleep]\n", basename(progname)); printf("2) %s -b byte_offset\n", basename(progname)); printf("3) %s -w byte_offset -v value\n\n", basename(progname)); puts("\t-r [-s sleep] : Dump EC registers"); puts("\t If sleep is given, sleep x seconds,"); puts("\t re-read EC registers and show changes"); puts("\t-b offset : Read value at byte_offset (in hex)"); puts("\t-w offset -v value : Write value at byte_offset"); puts("\t-h : Print this help\n\n"); puts("Offsets and values are in hexadecimal number system."); puts("The offset and value must be between 0 and 0xff."); exit(exit_status); } void parse_opts(int argc, char *argv[]) { int c; while ((c = getopt(argc, argv, "rs:b:w:v:h")) != -1) { switch (c) { case 'r': if (read_mode != -1) usage(argv[0], EXIT_FAILURE); read_mode = 1; break; case 's': if (read_mode != -1 && read_mode != 1) usage(argv[0], EXIT_FAILURE); sleep_time = atoi(optarg); if (sleep_time <= 0) { sleep_time = 0; usage(argv[0], EXIT_FAILURE); printf("Bad sleep time: %s\n", optarg); } break; case 'b': if (read_mode != -1) usage(argv[0], EXIT_FAILURE); read_mode = 1; read_byte_offset = strtoul(optarg, NULL, 16); break; case 'w': if (read_mode != -1) usage(argv[0], EXIT_FAILURE); read_mode = 0; write_byte_offset = strtoul(optarg, NULL, 16); break; case 'v': write_value = strtoul(optarg, NULL, 16); break; case 'h': usage(argv[0], EXIT_SUCCESS); default: fprintf(stderr, "Unknown option!\n"); usage(argv[0], EXIT_FAILURE); } } if (read_mode == 0) { if (write_byte_offset < 0 || write_byte_offset >= EC_SPACE_SIZE) { fprintf(stderr, "Wrong byte offset 0x%.2x, valid: " "[0-0x%.2x]\n", write_byte_offset, EC_SPACE_SIZE - 1); usage(argv[0], EXIT_FAILURE); } if (write_value < 0 || write_value >= 255) { fprintf(stderr, "Wrong byte offset 0x%.2x, valid:" "[0-0xff]\n", write_byte_offset); usage(argv[0], EXIT_FAILURE); } } if (read_mode == 1 && read_byte_offset != -1) { if (read_byte_offset < -1 || read_byte_offset >= EC_SPACE_SIZE) { fprintf(stderr, "Wrong byte offset 0x%.2x, valid: " "[0-0x%.2x]\n", read_byte_offset, EC_SPACE_SIZE - 1); usage(argv[0], EXIT_FAILURE); } } /* Add additional parameter checks here */ } void dump_ec(int fd) { char buf[EC_SPACE_SIZE]; char buf2[EC_SPACE_SIZE]; int byte_off, bytes_read; bytes_read = read(fd, buf, EC_SPACE_SIZE); if (bytes_read == -1) err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH); if (bytes_read != EC_SPACE_SIZE) fprintf(stderr, "Could only read %d bytes\n", bytes_read); printf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"); for (byte_off = 0; byte_off < bytes_read; byte_off++) { if ((byte_off % 16) == 0) printf("\n%.2X: ", byte_off); printf(" %.2x ", (uint8_t)buf[byte_off]); } printf("\n"); if (!sleep_time) return; printf("\n"); lseek(fd, 0, SEEK_SET); sleep(sleep_time); bytes_read = read(fd, buf2, EC_SPACE_SIZE); if (bytes_read == -1) err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH); if (bytes_read != EC_SPACE_SIZE) fprintf(stderr, "Could only read %d bytes\n", bytes_read); printf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"); for (byte_off = 0; byte_off < bytes_read; byte_off++) { if ((byte_off % 16) == 0) printf("\n%.2X: ", byte_off); if (buf[byte_off] == buf2[byte_off]) printf(" %.2x ", (uint8_t)buf2[byte_off]); else printf("*%.2x ", (uint8_t)buf2[byte_off]); } printf("\n"); } void read_ec_val(int fd, int byte_offset) { uint8_t buf; int error; error = lseek(fd, byte_offset, SEEK_SET); if (error != byte_offset) err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset); error = read(fd, &buf, 1); if (error != 1) err(EXIT_FAILURE, "Could not read byte 0x%.2x from %s\n", byte_offset, SYSFS_PATH); printf("0x%.2x\n", buf); return; } void write_ec_val(int fd, int byte_offset, uint8_t value) { int error; error = lseek(fd, byte_offset, SEEK_SET); if (error != byte_offset) err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset); error = write(fd, &value, 1); if (error != 1) err(EXIT_FAILURE, "Cannot write value 0x%.2x to offset 0x%.2x", value, byte_offset); } int main(int argc, char *argv[]) { int file_mode = O_RDONLY; int fd; parse_opts(argc, argv); if (read_mode == 0) file_mode = O_WRONLY; else if (read_mode == 1) file_mode = O_RDONLY; else usage(argv[0], EXIT_FAILURE); fd = open(SYSFS_PATH, file_mode); if (fd == -1) err(EXIT_FAILURE, "%s", SYSFS_PATH); if (read_mode) if (read_byte_offset == -1) dump_ec(fd); else if (read_byte_offset < 0 || read_byte_offset >= EC_SPACE_SIZE) usage(argv[0], EXIT_FAILURE); else read_ec_val(fd, read_byte_offset); else write_ec_val(fd, write_byte_offset, write_value); close(fd); exit(EXIT_SUCCESS); }