Remove FSF mailing address.
[openocd.git] / contrib / remote_bitbang / remote_bitbang_sysfsgpio.c
1 /***************************************************************************
2 * Copyright (C) 2013 Paul Fertser <fercerpav@gmail.com> *
3 * Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 /*
20 This is a test application to be used as a remote bitbang server for
21 the OpenOCD remote_bitbang interface driver.
22
23 To compile run:
24 gcc -Wall -ansi -pedantic -std=c99 -o remote_bitbang_sysfsgpio remote_bitbang_sysfsgpio.c
25
26
27 Usage example:
28
29 On Raspberry Pi run:
30 socat TCP6-LISTEN:7777,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
31
32 On host run:
33 openocd -c "interface remote_bitbang; remote_bitbang_host raspberrypi; remote_bitbang_port 7777" \
34 -f target/stm32f1x.cfg
35
36 Or if you want to test UNIX sockets, run both on Raspberry Pi:
37 socat UNIX-LISTEN:/tmp/remotebitbang-socket,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
38 openocd -c "interface remote_bitbang; remote_bitbang_host /tmp/remotebitbang-socket" -f target/stm32f1x.cfg
39 */
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <errno.h>
49
50 #define LOG_ERROR(...) do { \
51 fprintf(stderr, __VA_ARGS__); \
52 fputc('\n', stderr); \
53 } while (0)
54 #define LOG_WARNING(...) LOG_ERROR(__VA_ARGS__)
55
56 #define ERROR_OK (-1)
57 #define ERROR_FAIL (-2)
58 #define ERROR_JTAG_INIT_FAILED ERROR_FAIL
59
60 /*
61 * Helper func to determine if gpio number valid
62 *
63 * Assume here that there will be less than 1000 gpios on a system
64 */
65 static int is_gpio_valid(int gpio)
66 {
67 return gpio >= 0 && gpio < 1000;
68 }
69
70 /*
71 * Helper func to open, write to and close a file
72 * name and valstr must be null terminated.
73 *
74 * Returns negative on failure.
75 */
76 static int open_write_close(const char *name, const char *valstr)
77 {
78 int ret;
79 int fd = open(name, O_WRONLY);
80 if (fd < 0)
81 return fd;
82
83 ret = write(fd, valstr, strlen(valstr));
84 close(fd);
85
86 return ret;
87 }
88
89 /*
90 * Helper func to unexport gpio from sysfs
91 */
92 static void unexport_sysfs_gpio(int gpio)
93 {
94 char gpiostr[4];
95
96 if (!is_gpio_valid(gpio))
97 return;
98
99 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
100 if (open_write_close("/sys/class/gpio/unexport", gpiostr) < 0)
101 LOG_ERROR("Couldn't unexport gpio %d", gpio);
102
103 return;
104 }
105
106 /*
107 * Exports and sets up direction for gpio.
108 * If the gpio is an output, it is initialized according to init_high,
109 * otherwise it is ignored.
110 *
111 * If the gpio is already exported we just show a warning and continue; if
112 * openocd happened to crash (or was killed by user) then the gpios will not
113 * have been cleaned up.
114 */
115 static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
116 {
117 char buf[40];
118 char gpiostr[4];
119 int ret;
120
121 if (!is_gpio_valid(gpio))
122 return ERROR_OK;
123
124 snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
125 ret = open_write_close("/sys/class/gpio/export", gpiostr);
126 if (ret < 0) {
127 if (errno == EBUSY) {
128 LOG_WARNING("gpio %d is already exported", gpio);
129 } else {
130 LOG_ERROR("Couldn't export gpio %d", gpio);
131 perror("sysfsgpio: ");
132 return ERROR_FAIL;
133 }
134 }
135
136 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
137 ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
138 if (ret < 0) {
139 LOG_ERROR("Couldn't set direction for gpio %d", gpio);
140 perror("sysfsgpio: ");
141 unexport_sysfs_gpio(gpio);
142 return ERROR_FAIL;
143 }
144
145 snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
146 if (is_output)
147 ret = open(buf, O_WRONLY | O_NONBLOCK | O_SYNC);
148 else
149 ret = open(buf, O_RDONLY | O_NONBLOCK | O_SYNC);
150
151 if (ret < 0)
152 unexport_sysfs_gpio(gpio);
153
154 return ret;
155 }
156
157 /*
158 * file descriptors for /sys/class/gpio/gpioXX/value
159 * Set up during init.
160 */
161 static int tck_fd = -1;
162 static int tms_fd = -1;
163 static int tdi_fd = -1;
164 static int tdo_fd = -1;
165 static int trst_fd = -1;
166 static int srst_fd = -1;
167
168 /*
169 * Bitbang interface read of TDO
170 *
171 * The sysfs value will read back either '0' or '1'. The trick here is to call
172 * lseek to bypass buffering in the sysfs kernel driver.
173 */
174 static int sysfsgpio_read(void)
175 {
176 char buf[1];
177
178 /* important to seek to signal sysfs of new read */
179 lseek(tdo_fd, 0, SEEK_SET);
180 int ret = read(tdo_fd, &buf, sizeof(buf));
181
182 if (ret < 0) {
183 LOG_WARNING("reading tdo failed");
184 return 0;
185 }
186
187 return buf[0];
188 }
189
190 /*
191 * Bitbang interface write of TCK, TMS, TDI
192 *
193 * Seeing as this is the only function where the outputs are changed,
194 * we can cache the old value to avoid needlessly writing it.
195 */
196 static void sysfsgpio_write(int tck, int tms, int tdi)
197 {
198 const char one[] = "1";
199 const char zero[] = "0";
200
201 static int last_tck;
202 static int last_tms;
203 static int last_tdi;
204
205 static int first_time;
206 size_t bytes_written;
207
208 if (!first_time) {
209 last_tck = !tck;
210 last_tms = !tms;
211 last_tdi = !tdi;
212 first_time = 1;
213 }
214
215 if (tdi != last_tdi) {
216 bytes_written = write(tdi_fd, tdi ? &one : &zero, 1);
217 if (bytes_written != 1)
218 LOG_WARNING("writing tdi failed");
219 }
220
221 if (tms != last_tms) {
222 bytes_written = write(tms_fd, tms ? &one : &zero, 1);
223 if (bytes_written != 1)
224 LOG_WARNING("writing tms failed");
225 }
226
227 /* write clk last */
228 if (tck != last_tck) {
229 bytes_written = write(tck_fd, tck ? &one : &zero, 1);
230 if (bytes_written != 1)
231 LOG_WARNING("writing tck failed");
232 }
233
234 last_tdi = tdi;
235 last_tms = tms;
236 last_tck = tck;
237 }
238
239 /*
240 * Bitbang interface to manipulate reset lines SRST and TRST
241 *
242 * (1) assert or (0) deassert reset lines
243 */
244 static void sysfsgpio_reset(int trst, int srst)
245 {
246 const char one[] = "1";
247 const char zero[] = "0";
248 size_t bytes_written;
249
250 /* assume active low */
251 if (srst_fd >= 0) {
252 bytes_written = write(srst_fd, srst ? &zero : &one, 1);
253 if (bytes_written != 1)
254 LOG_WARNING("writing srst failed");
255 }
256
257 /* assume active low */
258 if (trst_fd >= 0) {
259 bytes_written = write(trst_fd, trst ? &zero : &one, 1);
260 if (bytes_written != 1)
261 LOG_WARNING("writing trst failed");
262 }
263 }
264
265 /* gpio numbers for each gpio. Negative values are invalid */
266 static int tck_gpio = -1;
267 static int tms_gpio = -1;
268 static int tdi_gpio = -1;
269 static int tdo_gpio = -1;
270 static int trst_gpio = -1;
271 static int srst_gpio = -1;
272
273 /* helper func to close and cleanup files only if they were valid/ used */
274 static void cleanup_fd(int fd, int gpio)
275 {
276 if (gpio >= 0) {
277 if (fd >= 0)
278 close(fd);
279
280 unexport_sysfs_gpio(gpio);
281 }
282 }
283
284 static void cleanup_all_fds(void)
285 {
286 cleanup_fd(tck_fd, tck_gpio);
287 cleanup_fd(tms_fd, tms_gpio);
288 cleanup_fd(tdi_fd, tdi_gpio);
289 cleanup_fd(tdo_fd, tdo_gpio);
290 cleanup_fd(trst_fd, trst_gpio);
291 cleanup_fd(srst_fd, srst_gpio);
292 }
293
294 static void process_remote_protocol(void)
295 {
296 int c;
297 while (1) {
298 c = getchar();
299 if (c == EOF || c == 'Q') /* Quit */
300 break;
301 else if (c == 'b' || c == 'B') /* Blink */
302 continue;
303 else if (c >= 'r' && c <= 'r' + 3) { /* Reset */
304 char d = c - 'r';
305 sysfsgpio_reset(!!(d & 2),
306 (d & 1));
307 } else if (c >= '0' && c <= '0' + 7) {/* Write */
308 char d = c - '0';
309 sysfsgpio_write(!!(d & 4),
310 !!(d & 2),
311 (d & 1));
312 } else if (c == 'R')
313 putchar(sysfsgpio_read());
314 else
315 LOG_ERROR("Unknown command '%c' received", c);
316 }
317 }
318
319 int main(int argc, char *argv[])
320 {
321 LOG_WARNING("SysfsGPIO remote_bitbang JTAG driver\n");
322
323 for (int i = 1; i < argc; i++) {
324 if (!strcmp(argv[i], "tck"))
325 tck_gpio = atoi(argv[++i]);
326 else if (!strcmp(argv[i], "tms"))
327 tms_gpio = atoi(argv[++i]);
328 else if (!strcmp(argv[i], "tdo"))
329 tdo_gpio = atoi(argv[++i]);
330 else if (!strcmp(argv[i], "tdi"))
331 tdi_gpio = atoi(argv[++i]);
332 else if (!strcmp(argv[i], "trst"))
333 trst_gpio = atoi(argv[++i]);
334 else if (!strcmp(argv[i], "srst"))
335 srst_gpio = atoi(argv[++i]);
336 else {
337 LOG_ERROR("Usage:\n%s ((tck|tms|tdo|tdi|trst|srst) num)*", argv[0]);
338 return -1;
339 }
340 }
341
342 if (!(is_gpio_valid(tck_gpio)
343 && is_gpio_valid(tms_gpio)
344 && is_gpio_valid(tdi_gpio)
345 && is_gpio_valid(tdo_gpio))) {
346 if (!is_gpio_valid(tck_gpio))
347 LOG_ERROR("gpio num for tck is invalid");
348 if (!is_gpio_valid(tms_gpio))
349 LOG_ERROR("gpio num for tms is invalid");
350 if (!is_gpio_valid(tdo_gpio))
351 LOG_ERROR("gpio num for tdo is invalid");
352 if (!is_gpio_valid(tdi_gpio))
353 LOG_ERROR("gpio num for tdi is invalid");
354
355 LOG_ERROR("Require tck, tms, tdi and tdo gpios to all be specified");
356 return ERROR_JTAG_INIT_FAILED;
357 }
358
359 /*
360 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
361 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
362 */
363 tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
364 if (tck_fd < 0)
365 goto out_error;
366
367 tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
368 if (tms_fd < 0)
369 goto out_error;
370
371 tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
372 if (tdi_fd < 0)
373 goto out_error;
374
375 tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
376 if (tdo_fd < 0)
377 goto out_error;
378
379 /* assume active low */
380 if (trst_gpio > 0) {
381 trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
382 if (trst_fd < 0)
383 goto out_error;
384 }
385
386 /* assume active low */
387 if (srst_gpio > 0) {
388 srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
389 if (srst_fd < 0)
390 goto out_error;
391 }
392
393 LOG_WARNING("SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
394 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
395 LOG_WARNING("SysfsGPIO num: srst = %d", srst_gpio);
396 LOG_WARNING("SysfsGPIO num: trst = %d", trst_gpio);
397
398 setvbuf(stdout, NULL, _IONBF, 0);
399 process_remote_protocol();
400
401 cleanup_all_fds();
402 return 0;
403 out_error:
404 cleanup_all_fds();
405 return ERROR_JTAG_INIT_FAILED;
406 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)