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