summaryrefslogtreecommitdiff
path: root/util/ec_uartd.c
blob: 4fc032361989bf5072806f19105cffc6397982db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* Copyright 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
/* ec_uartd.c - UART daemon for BD-ICDI-B board for EC debugging
 *
 * based on chromeos_public/src/third_party/hdctools/src/ftdiuart.c
 *
 * compile with:
 *    gcc -o ftdi_uartd ftdi_uartd.c -lftdi
 */

/* Force header files to define grantpt(), posix_openpt(), cfmakeraw() */
#define _DEFAULT_SOURCE /* Newer glibc */
#define _BSD_SOURCE     /* Older glibc */
#define _XOPEN_SOURCE 600
/* Force header file to declare ptsname_r(), etc. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <errno.h>
#include <fcntl.h>
#include <ftdi.h>
#include <getopt.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>

static const int ftdi_id[][2] = { {0x0403, 0xbcda},
				  {0x0403, 0xbcd9} };

int main(int argc, char **argv)
{
	struct ftdi_context fcontext;
	struct termios tty_cfg;
	char ptname[PATH_MAX];
	char buf[1024];
	int fd;
	int rv;
	int i;
	int device_opened;

	/* Init */
	if (ftdi_init(&fcontext) < 0) {
		fprintf(stderr, "ftdi_init failed\n");
		return 1;
	}

	/* Open interface B (UART) in the FTDI device and set 115kbaud */
	ftdi_set_interface(&fcontext, INTERFACE_B);
	device_opened = 0;
	for (i = 0; i < sizeof(ftdi_id) / sizeof(ftdi_id[0]); ++i) {
		rv = ftdi_usb_open(&fcontext, ftdi_id[i][0], ftdi_id[i][1]);
		if (rv >= 0) {
			fprintf(stderr, "Using FTDI device %04x:%04x\n",
					ftdi_id[i][0], ftdi_id[i][1]);
			device_opened = 1;
			break;
		}
	}
	if (!device_opened) {
		fprintf(stderr, "error opening ftdi device: %d (%s)\n",
			rv, ftdi_get_error_string(&fcontext));
		return 2;
	}

	rv = ftdi_set_baudrate(&fcontext, 115200);
	if (rv < 0) {
		fprintf(stderr, "error setting baudrate: %d (%s)\n",
			rv, ftdi_get_error_string(&fcontext));
		return 2;
	}

	/* Set DTR; this muxes RX on the ICDI board */
	ftdi_setdtr(&fcontext, 1);

	/* Open the pty */
	fd = posix_openpt(O_RDWR | O_NOCTTY);
	if (fd == -1) {
		perror("opening pty master");
		return 3;
	}
	if (grantpt(fd) == -1) {
		perror("grantpt");
		return 3;
	}
	if (unlockpt(fd) == -1) {
		perror("unlockpt");
		return 3;
	}
	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
		perror("fcntl setfl -> nonblock");
		return 3;
	}
	if (ptsname_r(fd, ptname, PATH_MAX) != 0) {
		perror("getting name of pty");
		return 3;
	}
	fprintf(stderr, "pty name = %s\n", ptname);
	if (!isatty(fd)) {
		perror("not a TTY device\n");
		return 3;
	}
	cfmakeraw(&tty_cfg);
	tcsetattr(fd, TCSANOW, &tty_cfg);
	if (chmod(ptname, 0666) == -1) {
		perror("setting pty attributes");
		return 3;
	}

	/* Read and write data forever */
	while (1) {
		int bytes = read(fd, buf, sizeof(buf));
		if (bytes > 0) {
			rv = ftdi_write_data(&fcontext, buf, bytes);
			if (rv != bytes) {
				perror("writing to uart");
				break;
			}
		}

		usleep(1000);

		bytes = ftdi_read_data(&fcontext, buf, sizeof(buf));
		if (bytes > 0) {
			int bytes_remaining = bytes;
			char *buf_ptr = buf;

retry_write:
			while (bytes_remaining &&
			       ((bytes = write(fd, buf_ptr,
					       bytes_remaining)) > 0)) {
				buf_ptr += bytes;
				bytes_remaining -= bytes;
			}

			if ((bytes == -1) &&
			    ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
				goto retry_write;

			if (bytes == -1)
				perror("writing ftdi data to pty");

		} else if (bytes < 0) {
			perror("failed ftdi_read_data");
			break;
		}
	}

	/* Cleanup */
	close(fd);
	ftdi_usb_close(&fcontext);
	ftdi_deinit(&fcontext);
	return 0;
}