clang: Fixes of warning and nasty bugs
[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 <sys/select.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->in, value);
59 }
60
61 // try to empty our fifo, the uart_pty_xoff_hook() will be called when
62 // other side is full
63 static void
64 uart_pty_flush_incoming(
65                 uart_pty_t * p)
66 {
67         while (p->xon && !uart_pty_fifo_isempty(&p->out)) {
68                 uint8_t byte = uart_pty_fifo_read(&p->out);
69                 TRACE(printf("uart_pty_flush_incoming send %02x\n", byte);)
70                 avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
71         }
72 }
73
74 /*
75  * Called when the uart has room in it's input buffer. This is called repeateadly
76  * if necessary, while the xoff is called only when the uart fifo is FULL
77  */
78 static void
79 uart_pty_xon_hook(
80                 struct avr_irq_t * irq,
81                 uint32_t value,
82                 void * param)
83 {
84         uart_pty_t * p = (uart_pty_t*)param;
85         TRACE(if (!p->xon) printf("uart_pty_xon_hook\n");)
86         p->xon = 1;
87         uart_pty_flush_incoming(p);
88 }
89
90 /*
91  * Called when the uart ran out of room in it's input buffer
92  */
93 static void
94 uart_pty_xoff_hook(
95                 struct avr_irq_t * irq,
96                 uint32_t value,
97                 void * param)
98 {
99         uart_pty_t * p = (uart_pty_t*)param;
100         TRACE(if (p->xon) printf("uart_pty_xoff_hook\n");)
101         p->xon = 0;
102 }
103
104 static void *
105 uart_pty_thread(
106                 void * param)
107 {
108         uart_pty_t * p = (uart_pty_t*)param;
109
110         while (1) {
111                 fd_set read_set, write_set;
112                 int max = p->s + 1;
113                 FD_ZERO(&read_set);
114                 FD_ZERO(&write_set);
115
116                 // read more only if buffer was flushed
117                 if (p->buffer_len == p->buffer_done)
118                         FD_SET(p->s, &read_set);
119                 if (!uart_pty_fifo_isempty(&p->in))
120                         FD_SET(p->s, &write_set);
121
122                 struct timeval timo = { 0, 500 };       // short, but not too short interval
123                 int ret = select(max, &read_set, &write_set, NULL, &timo);
124
125                 if (!ret)
126                         continue;
127                 if (ret < 0)
128                         break;
129
130                 if (FD_ISSET(p->s, &read_set)) {
131                         ssize_t r = read(p->s, p->buffer, sizeof(p->buffer)-1);
132                         p->buffer_len = r;
133                         p->buffer_done = 0;
134                         TRACE(hdump("pty recv", p->buffer, r);)
135                 }
136                 if (p->buffer_done < p->buffer_len) {
137                         // write them in fifo
138                         while (p->buffer_done < p->buffer_len && !uart_pty_fifo_isfull(&p->out))
139                                 uart_pty_fifo_write(&p->out, p->buffer[p->buffer_done++]);
140                 }
141                 if (FD_ISSET(p->s, &write_set)) {
142                         uint8_t buffer[512];
143                         // write them in fifo
144                         uint8_t * dst = buffer;
145                         while (!uart_pty_fifo_isempty(&p->in) && dst < (buffer+sizeof(buffer)))
146                                 *dst++ = uart_pty_fifo_read(&p->in);
147                         size_t len = dst - buffer;
148                         TRACE(size_t r =) write(p->s, buffer, len);
149                         TRACE(hdump("pty send", buffer, r);)
150                 }
151                 uart_pty_flush_incoming(p);
152         }
153         return NULL;
154 }
155
156 static const char * irq_names[IRQ_UART_PTY_COUNT] = {
157         [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
158         [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
159 };
160
161 void
162 uart_pty_init(
163                 struct avr_t * avr,
164                 uart_pty_t * p)
165 {
166         p->avr = avr;
167         p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
168         avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
169
170         int m, s;
171
172         if (openpty(&m, &s, p->slavename, NULL, NULL) < 0) {
173                 fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
174                 return ;
175         }
176         p->s = m;
177
178         printf("uart_pty_init bridge on port *** %s ***\n", p->slavename);
179
180         pthread_create(&p->thread, NULL, uart_pty_thread, p);
181
182 }
183
184 void
185 uart_pty_stop(
186                 uart_pty_t * p)
187 {
188         puts(__func__);
189         pthread_kill(p->thread, SIGINT);
190         close(p->s);
191         void * ret;
192         pthread_join(p->thread, &ret);
193 }
194
195 void
196 uart_pty_connect(
197                 uart_pty_t * p,
198                 char uart)
199 {
200         // disable the stdio dump, as we are sending binary there
201         uint32_t f = 0;
202         avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
203         f &= ~AVR_UART_FLAG_STDIO;
204         avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
205
206         avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
207         avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
208         avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
209         avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
210         if (src && dst) {
211                 avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
212                 avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
213         }
214         if (xon)
215                 avr_irq_register_notify(xon, uart_pty_xon_hook, p);
216         if (xoff)
217                 avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
218
219         char link[128];
220         sprintf(link, "/tmp/simavr-uart%c", uart);
221         unlink(link);
222         if (symlink(p->slavename, link) != 0) {
223                 fprintf(stderr, "WARN %s: Can't create %s: %s", __func__, link, strerror(errno));
224         } else {
225                 printf("%s: %s now points to %s\n", __func__, link, p->slavename);
226         }
227         if (getenv("SIMAVR_UART_XTERM") && atoi(getenv("SIMAVR_UART_XTERM"))) {
228                 char cmd[256];
229                 sprintf(cmd, "nohup xterm -e picocom -b 115200 %s >/dev/null 2>&1 &", p->slavename);
230                 system(cmd);
231         } else
232                 printf("note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal\n");
233 }
234