diff options
author | Thomas Renninger <trenn@suse.de> | 2014-04-07 09:16:57 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-04-28 06:36:56 -0400 |
commit | d7cddbb07b18cc45e57420005850ca0326b9e074 (patch) | |
tree | 7d640a12341524d0227c7829e5ec990dd378ad13 /tools/power | |
parent | d1db0eea852497762cab43b905b879dfcd3b8987 (diff) |
ACPI / tools: Introduce ec_access.c - tool to access the EC
This userspace tool accesses the EC through the ec_sys debug driver
(through /sys/kernel/debug/ec/ec0/io).
The EC command/data registers cannot be accessed directly, because they
may be manipulated by the AML interpreter in parallel.
The ec_sys driver synchronizes user space (debug) access with the AML
interpreter.
Signed-off-by: Thomas Renninger <trenn@suse.de>
[rjw: Changelog]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'tools/power')
-rw-r--r-- | tools/power/acpi/Makefile | 2 | ||||
-rw-r--r-- | tools/power/acpi/tools/ec/Makefile | 22 | ||||
-rw-r--r-- | tools/power/acpi/tools/ec/ec_access.c | 238 |
3 files changed, 262 insertions, 0 deletions
diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile index c2c0f20067a5..c225d6d9fb29 100644 --- a/tools/power/acpi/Makefile +++ b/tools/power/acpi/Makefile | |||
@@ -19,6 +19,8 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) | |||
19 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) | 19 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) |
20 | endif | 20 | endif |
21 | 21 | ||
22 | SUBDIRS = tools/ec | ||
23 | |||
22 | # --- CONFIGURATION BEGIN --- | 24 | # --- CONFIGURATION BEGIN --- |
23 | 25 | ||
24 | # Set the following to `true' to make a unstripped, unoptimized | 26 | # Set the following to `true' to make a unstripped, unoptimized |
diff --git a/tools/power/acpi/tools/ec/Makefile b/tools/power/acpi/tools/ec/Makefile new file mode 100644 index 000000000000..b7b0b929bd32 --- /dev/null +++ b/tools/power/acpi/tools/ec/Makefile | |||
@@ -0,0 +1,22 @@ | |||
1 | ec_access: ec_access.o | ||
2 | $(ECHO) " LD " $@ | ||
3 | $(QUIET) $(LD) $(CFLAGS) $(LDFLAGS) $< -o $@ | ||
4 | $(QUIET) $(STRIPCMD) $@ | ||
5 | |||
6 | %.o: %.c | ||
7 | $(ECHO) " CC " $@ | ||
8 | $(QUIET) $(CC) -c $(CFLAGS) -o $@ $< | ||
9 | |||
10 | all: ec_access | ||
11 | |||
12 | install: | ||
13 | $(INSTALL) -d $(DESTDIR)${sbindir} | ||
14 | $(INSTALL_PROGRAM) ec_access $(DESTDIR)${sbindir} | ||
15 | |||
16 | uninstall: | ||
17 | - rm -f $(DESTDIR)${sbindir}/ec_access | ||
18 | |||
19 | clean: | ||
20 | -rm -f $(OUTPUT)ec_access | ||
21 | |||
22 | .PHONY: all install uninstall | ||
diff --git a/tools/power/acpi/tools/ec/ec_access.c b/tools/power/acpi/tools/ec/ec_access.c new file mode 100644 index 000000000000..6b8aaed44f2c --- /dev/null +++ b/tools/power/acpi/tools/ec/ec_access.c | |||
@@ -0,0 +1,238 @@ | |||
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 | |||
30 | static int read_mode = -1; | ||
31 | static int sleep_time; | ||
32 | static int write_byte_offset = -1; | ||
33 | static int read_byte_offset = -1; | ||
34 | static uint8_t write_value = -1; | ||
35 | |||
36 | void 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 | |||
54 | void 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 | |||
126 | void 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 | |||
176 | void 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 | |||
193 | void 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 | |||
207 | int 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 | } | ||