1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * ec_access.c
4 *
5 * Copyright (C) 2010 SUSE Linux Products GmbH
6 * Author:
7 *      Thomas Renninger <trenn@suse.de>
8 */
9
10#include <fcntl.h>
11#include <err.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <libgen.h>
15#include <unistd.h>
16#include <getopt.h>
17#include <stdint.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20
21
22#define EC_SPACE_SIZE 256
23#define SYSFS_PATH "/sys/kernel/debug/ec/ec0/io"
24
25/* TBD/Enhancements:
26   - Provide param for accessing different ECs (not supported by kernel yet)
27*/
28
29static int read_mode = -1;
30static int sleep_time;
31static int write_byte_offset = -1;
32static int read_byte_offset = -1;
33static uint8_t write_value = -1;
34
35void usage(char progname[], int exit_status)
36{
37	printf("Usage:\n");
38	printf("1) %s -r [-s sleep]\n", basename(progname));
39	printf("2) %s -b byte_offset\n", basename(progname));
40	printf("3) %s -w byte_offset -v value\n\n", basename(progname));
41
42	puts("\t-r [-s sleep]      : Dump EC registers");
43	puts("\t                     If sleep is given, sleep x seconds,");
44	puts("\t                     re-read EC registers and show changes");
45	puts("\t-b offset          : Read value at byte_offset (in hex)");
46	puts("\t-w offset -v value : Write value at byte_offset");
47	puts("\t-h                 : Print this help\n\n");
48	puts("Offsets and values are in hexadecimal number system.");
49	puts("The offset and value must be between 0 and 0xff.");
50	exit(exit_status);
51}
52
53void parse_opts(int argc, char *argv[])
54{
55	int c;
56
57	while ((c = getopt(argc, argv, "rs:b:w:v:h")) != -1) {
58
59		switch (c) {
60		case 'r':
61			if (read_mode != -1)
62				usage(argv[0], EXIT_FAILURE);
63			read_mode = 1;
64			break;
65		case 's':
66			if (read_mode != -1 && read_mode != 1)
67				usage(argv[0], EXIT_FAILURE);
68
69			sleep_time = atoi(optarg);
70			if (sleep_time <= 0) {
71				sleep_time = 0;
72				usage(argv[0], EXIT_FAILURE);
73				printf("Bad sleep time: %s\n", optarg);
74			}
75			break;
76		case 'b':
77			if (read_mode != -1)
78				usage(argv[0], EXIT_FAILURE);
79			read_mode = 1;
80			read_byte_offset = strtoul(optarg, NULL, 16);
81			break;
82		case 'w':
83			if (read_mode != -1)
84				usage(argv[0], EXIT_FAILURE);
85			read_mode = 0;
86			write_byte_offset = strtoul(optarg, NULL, 16);
87			break;
88		case 'v':
89			write_value = strtoul(optarg, NULL, 16);
90			break;
91		case 'h':
92			usage(argv[0], EXIT_SUCCESS);
93		default:
94			fprintf(stderr, "Unknown option!\n");
95			usage(argv[0], EXIT_FAILURE);
96		}
97	}
98	if (read_mode == 0) {
99		if (write_byte_offset < 0 ||
100		    write_byte_offset >= EC_SPACE_SIZE) {
101			fprintf(stderr, "Wrong byte offset 0x%.2x, valid: "
102				"[0-0x%.2x]\n",
103				write_byte_offset, EC_SPACE_SIZE - 1);
104			usage(argv[0], EXIT_FAILURE);
105		}
106		if (write_value < 0 ||
107		    write_value >= 255) {
108			fprintf(stderr, "Wrong byte offset 0x%.2x, valid:"
109				"[0-0xff]\n", write_byte_offset);
110			usage(argv[0], EXIT_FAILURE);
111		}
112	}
113	if (read_mode == 1 && read_byte_offset != -1) {
114		if (read_byte_offset < -1 ||
115		    read_byte_offset >= EC_SPACE_SIZE) {
116			fprintf(stderr, "Wrong byte offset 0x%.2x, valid: "
117				"[0-0x%.2x]\n",
118				read_byte_offset, EC_SPACE_SIZE - 1);
119			usage(argv[0], EXIT_FAILURE);
120		}
121	}
122	/* Add additional parameter checks here */
123}
124
125void dump_ec(int fd)
126{
127	char buf[EC_SPACE_SIZE];
128	char buf2[EC_SPACE_SIZE];
129	int byte_off, bytes_read;
130
131	bytes_read = read(fd, buf, EC_SPACE_SIZE);
132
133	if (bytes_read == -1)
134		err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH);
135
136	if (bytes_read != EC_SPACE_SIZE)
137		fprintf(stderr, "Could only read %d bytes\n", bytes_read);
138
139	printf("     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F");
140	for (byte_off = 0; byte_off < bytes_read; byte_off++) {
141		if ((byte_off % 16) == 0)
142			printf("\n%.2X: ", byte_off);
143		printf(" %.2x ", (uint8_t)buf[byte_off]);
144	}
145	printf("\n");
146
147	if (!sleep_time)
148		return;
149
150	printf("\n");
151	lseek(fd, 0, SEEK_SET);
152	sleep(sleep_time);
153
154	bytes_read = read(fd, buf2, EC_SPACE_SIZE);
155
156	if (bytes_read == -1)
157		err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH);
158
159	if (bytes_read != EC_SPACE_SIZE)
160		fprintf(stderr, "Could only read %d bytes\n", bytes_read);
161
162	printf("     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F");
163	for (byte_off = 0; byte_off < bytes_read; byte_off++) {
164		if ((byte_off % 16) == 0)
165			printf("\n%.2X: ", byte_off);
166
167		if (buf[byte_off] == buf2[byte_off])
168			printf(" %.2x ", (uint8_t)buf2[byte_off]);
169		else
170			printf("*%.2x ", (uint8_t)buf2[byte_off]);
171	}
172	printf("\n");
173}
174
175void read_ec_val(int fd, int byte_offset)
176{
177	uint8_t buf;
178	int error;
179
180	error = lseek(fd, byte_offset, SEEK_SET);
181	if (error != byte_offset)
182		err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset);
183
184	error = read(fd, &buf, 1);
185	if (error != 1)
186		err(EXIT_FAILURE, "Could not read byte 0x%.2x from %s\n",
187		    byte_offset, SYSFS_PATH);
188	printf("0x%.2x\n", buf);
189	return;
190}
191
192void write_ec_val(int fd, int byte_offset, uint8_t value)
193{
194	int error;
195
196	error = lseek(fd, byte_offset, SEEK_SET);
197	if (error != byte_offset)
198		err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset);
199
200	error = write(fd, &value, 1);
201	if (error != 1)
202		err(EXIT_FAILURE, "Cannot write value 0x%.2x to offset 0x%.2x",
203		    value, byte_offset);
204}
205
206int main(int argc, char *argv[])
207{
208	int file_mode = O_RDONLY;
209	int fd;
210
211	parse_opts(argc, argv);
212
213	if (read_mode == 0)
214		file_mode = O_WRONLY;
215	else if (read_mode == 1)
216		file_mode = O_RDONLY;
217	else
218		usage(argv[0], EXIT_FAILURE);
219
220	fd = open(SYSFS_PATH, file_mode);
221	if (fd == -1)
222		err(EXIT_FAILURE, "%s", SYSFS_PATH);
223
224	if (read_mode)
225		if (read_byte_offset == -1)
226			dump_ec(fd);
227		else if (read_byte_offset < 0 ||
228			 read_byte_offset >= EC_SPACE_SIZE)
229			usage(argv[0], EXIT_FAILURE);
230		else
231			read_ec_val(fd, read_byte_offset);
232	else
233		write_ec_val(fd, write_byte_offset, write_value);
234	close(fd);
235
236	exit(EXIT_SUCCESS);
237}
238