Merge pull request #10 from the-real-orca/mingw
[simavr] / examples / parts / uart_pty.c
1 /*
2         uart_pty.c
3
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "sim_network.h"
23 #include <stdlib.h>
24 #include <pthread.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #ifdef __APPLE__
31 #include <util.h>
32 #else
33 #include <pty.h>
34 #endif
35
36 #include "uart_pty.h"
37 #include "avr_uart.h"
38 #include "sim_hex.h"
39
40 DEFINE_FIFO(uint8_t,uart_pty_fifo);
41
42 //#define TRACE(_w) _w
43 #ifndef TRACE
44 #define TRACE(_w)
45 #endif
46
47 /*
48  * called when a byte is send via the uart on the AVR
49  */
50 static void
51 uart_pty_in_hook(
52                 struct avr_irq_t * irq,
53                 uint32_t value,
54                 void * param)
55 {
56         uart_pty_t * p = (uart_pty_t*)param;
57         TRACE(printf("uart_pty_in_hook %02x\n", value);)
58         uart_pty_fifo_write(&p->pty.in, value);
59
60         if (p->tap.s) {
61                 if (p->tap.crlf && value == '\n')
62                         uart_pty_fifo_write(&p->tap.in, '\r');
63                 uart_pty_fifo_write(&p->tap.in, value);
64         }
65 }
66
67 // try to empty our fifo, the uart_pty_xoff_hook() will be called when
68 // other side is full
69 static void
70 uart_pty_flush_incoming(
71                 uart_pty_t * p)
72 {
73         while (p->xon && !uart_pty_fifo_isempty(&p->pty.out)) {
74                 uint8_t byte = uart_pty_fifo_read(&p->pty.out);
75                 TRACE(printf("uart_pty_flush_incoming send %02x\n", byte);)
76                 avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
77
78                 if (p->tap.s) {
79                         if (p->tap.crlf && byte == '\n')
80                                 uart_pty_fifo_write(&p->tap.in, '\r');
81                         uart_pty_fifo_write(&p->tap.in, byte);
82                 }
83         }
84         if (p->tap.s) {
85                 while (p->xon && !uart_pty_fifo_isempty(&p->tap.out)) {
86                         uint8_t byte = uart_pty_fifo_read(&p->tap.out);
87                         if (p->tap.crlf && byte == '\r') {
88                                 uart_pty_fifo_write(&p->tap.in, '\n');
89                         }
90                         if (byte == '\n')
91                                 continue;
92                         uart_pty_fifo_write(&p->tap.in, byte);
93                         avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
94                 }
95         }
96 }
97
98 /*
99  * Called when the uart has room in it's input buffer. This is called repeateadly
100  * if necessary, while the xoff is called only when the uart fifo is FULL
101  */
102 static void
103 uart_pty_xon_hook(
104                 struct avr_irq_t * irq,
105                 uint32_t value,
106                 void * param)
107 {
108         uart_pty_t * p = (uart_pty_t*)param;
109         TRACE(if (!p->xon) printf("uart_pty_xon_hook\n");)
110         p->xon = 1;
111         uart_pty_flush_incoming(p);
112 }
113
114 /*
115  * Called when the uart ran out of room in it's input buffer
116  */
117 static void
118 uart_pty_xoff_hook(
119                 struct avr_irq_t * irq,
120                 uint32_t value,
121                 void * param)
122 {
123         uart_pty_t * p = (uart_pty_t*)param;
124         TRACE(if (p->xon) printf("uart_pty_xoff_hook\n");)
125         p->xon = 0;
126 }
127
128 static void *
129 uart_pty_thread(
130                 void * param)
131 {
132         uart_pty_t * p = (uart_pty_t*)param;
133
134         while (1) {
135                 fd_set read_set, write_set;
136                 int max = 0;
137                 FD_ZERO(&read_set);
138                 FD_ZERO(&write_set);
139
140                 for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
141                         // read more only if buffer was flushed
142                         if (p->port[ti].buffer_len == p->port[ti].buffer_done) {
143                                 FD_SET(p->port[ti].s, &read_set);
144                                 max = p->port[ti].s > max ? p->port[ti].s : max;
145                         }
146                         if (!uart_pty_fifo_isempty(&p->port[ti].in)) {
147                                 FD_SET(p->port[ti].s, &write_set);
148                                 max = p->port[ti].s > max ? p->port[ti].s : max;
149                         }
150                 }
151
152                 struct timeval timo = { 0, 500 };       // short, but not too short interval
153                 int ret = select(max+1, &read_set, &write_set, NULL, &timo);
154
155                 if (!ret)
156                         continue;
157                 if (ret < 0)
158                         break;
159
160                 for (int ti = 0; ti < 2; ti++) if (p->port[ti].s) {
161                         if (FD_ISSET(p->port[ti].s, &read_set)) {
162                                 ssize_t r = read(p->port[ti].s, p->port[ti].buffer, sizeof(p->port[ti].buffer)-1);
163                                 p->port[ti].buffer_len = r;
164                                 p->port[ti].buffer_done = 0;
165                                 TRACE(hdump("pty recv", p->port[ti].buffer, r);)
166                         }
167                         if (p->port[ti].buffer_done < p->port[ti].buffer_len) {
168                                 // write them in fifo
169                                 while (p->port[ti].buffer_done < p->port[ti].buffer_len &&
170                                                 !uart_pty_fifo_isfull(&p->port[ti].out))
171                                         uart_pty_fifo_write(&p->port[ti].out,
172                                                         p->port[ti].buffer[p->port[ti].buffer_done++]);
173                         }
174                         if (FD_ISSET(p->port[ti].s, &write_set)) {
175                                 uint8_t buffer[512];
176                                 // write them in fifo
177                                 uint8_t * dst = buffer;
178                                 while (!uart_pty_fifo_isempty(&p->port[ti].in) &&
179                                                 dst < (buffer + sizeof(buffer)))
180                                         *dst++ = uart_pty_fifo_read(&p->port[ti].in);
181                                 size_t len = dst - buffer;
182                                 TRACE(size_t r =) write(p->port[ti].s, buffer, len);
183                                 TRACE(hdump("pty send", buffer, r);)
184                         }
185                 }
186                 uart_pty_flush_incoming(p);
187         }
188         return NULL;
189 }
190
191 static const char * irq_names[IRQ_UART_PTY_COUNT] = {
192         [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
193         [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
194 };
195
196 void
197 uart_pty_init(
198                 struct avr_t * avr,
199                 uart_pty_t * p)
200 {
201         memset(p, 0, sizeof(*p));
202
203         p->avr = avr;
204         p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
205         avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
206
207         int hastap = (getenv("SIMAVR_UART_TAP") && atoi(getenv("SIMAVR_UART_TAP"))) ||
208                         (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) ;
209
210         for (int ti = 0; ti < 1 + hastap; ti++) {
211                 int m, s;
212
213                 if (openpty(&m, &s, p->port[ti].slavename, NULL, NULL) < 0) {
214                         fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
215                         return ;
216                 }
217                 struct termios tio;
218                 tcgetattr(m, &tio);
219                 cfmakeraw(&tio);
220                 tcsetattr(m, TCSANOW, &tio);
221                 p->port[ti].s = m;
222                 p->port[ti].tap = ti != 0;
223                 p->port[ti].crlf = ti != 0;
224                 printf("uart_pty_init %s on port *** %s ***\n",
225                                 ti == 0 ? "bridge" : "tap", p->port[ti].slavename);
226         }
227
228         pthread_create(&p->thread, NULL, uart_pty_thread, p);
229
230 }
231
232 void
233 uart_pty_stop(
234                 uart_pty_t * p)
235 {
236         puts(__func__);
237         pthread_kill(p->thread, SIGINT);
238         for (int ti = 0; ti < 2; ti++)
239                 if (p->port[ti].s)
240                         close(p->port[ti].s);
241         void * ret;
242         pthread_join(p->thread, &ret);
243 }
244
245 void
246 uart_pty_connect(
247                 uart_pty_t * p,
248                 char uart)
249 {
250         // disable the stdio dump, as we are sending binary there
251         uint32_t f = 0;
252         avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
253         f &= ~AVR_UART_FLAG_STDIO;
254         avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
255
256         avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
257         avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
258         avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
259         avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
260         if (src && dst) {
261                 avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
262                 avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
263         }
264         if (xon)
265                 avr_irq_register_notify(xon, uart_pty_xon_hook, p);
266         if (xoff)
267                 avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
268
269         for (int ti = 0; ti < 1; ti++) if (p->port[ti].s) {
270                 char link[128];
271                 sprintf(link, "/tmp/simavr-uart%s%c", ti == 1 ? "tap" : "", uart);
272                 unlink(link);
273                 if (symlink(p->port[ti].slavename, link) != 0) {
274                         fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno));
275                 } else {
276                         printf("%s: %s now points to %s\n", __func__, link, p->port[ti].slavename);
277                 }
278         }
279         if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) {
280                 char cmd[256];
281                 sprintf(cmd, "xterm -e picocom -b 115200 %s >/dev/null 2>&1 &",
282                                 p->tap.slavename);
283                 system(cmd);
284         } else
285                 printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n");
286 }
287