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 
29 static int read_mode = -1;
30 static int sleep_time;
31 static int write_byte_offset = -1;
32 static int read_byte_offset = -1;
33 static uint8_t write_value = -1;
34 
usage(char progname[],int exit_status)35 void 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 
parse_opts(int argc,char * argv[])53 void 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 
dump_ec(int fd)125 void 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 
read_ec_val(int fd,int byte_offset)175 void 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 
write_ec_val(int fd,int byte_offset,uint8_t value)192 void 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 
main(int argc,char * argv[])206 int 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