uart_pty: New part
[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 <pthread.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <pty.h>
29 #include <signal.h>
30
31 #include "uart_pty.h"
32 #include "avr_uart.h"
33 #include "sim_hex.h"
34
35 DEFINE_FIFO(uint8_t,uart_pty_fifo);
36
37 /*
38  * called when a byte is send via the uart on the AVR
39  */
40 static void uart_pty_in_hook(struct avr_irq_t * irq, uint32_t value, void * param)
41 {
42         uart_pty_t * p = (uart_pty_t*)param;
43         //printf("uart_pty_in_hook %02x\n", value);
44         uart_pty_fifo_write(&p->in, value);
45 }
46
47 // try to empty our fifo, the uart_pty_xoff_hook() will be called when
48 // other side is full
49 static void  uart_pty_flush_incoming(uart_pty_t * p)
50 {
51         while (p->xon && !uart_pty_fifo_isempty(&p->out)) {
52                 uint8_t byte = uart_pty_fifo_read(&p->out);
53         //      printf("uart_pty_flush_incoming send %02x\n", byte);
54                 avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
55         }
56 }
57
58 /*
59  * Called when the uart has room in it's input buffer. This is called repeateadly
60  * if necessary, while the xoff is called only when the uart fifo is FULL
61  */
62 static void uart_pty_xon_hook(struct avr_irq_t * irq, uint32_t value, void * param)
63 {
64         uart_pty_t * p = (uart_pty_t*)param;
65         if (!p->xon)
66                 printf("uart_pty_xon_hook\n");
67         p->xon = 1;
68         uart_pty_flush_incoming(p);
69 }
70
71 /*
72  * Called when the uart ran out of room in it's input buffer
73  */
74 static void uart_pty_xoff_hook(struct avr_irq_t * irq, uint32_t value, void * param)
75 {
76         uart_pty_t * p = (uart_pty_t*)param;
77         if (p->xon)
78                 printf("uart_pty_xoff_hook\n");
79         p->xon = 0;
80 }
81
82 static void * uart_pty_thread(void * param)
83 {
84         uart_pty_t * p = (uart_pty_t*)param;
85
86         while (1) {
87                 fd_set read_set, write_set;
88                 int max = p->s + 1;
89                 FD_ZERO(&read_set);
90                 FD_ZERO(&write_set);
91
92                 // read more only if buffer was flushed
93                 if (p->buffer_len == p->buffer_done)
94                         FD_SET(p->s, &read_set);
95                 if (!uart_pty_fifo_isempty(&p->in))
96                         FD_SET(p->s, &write_set);
97
98                 struct timeval timo = { 0, 500 };       // short, but not too short interval
99                 int ret = select(max, &read_set, &write_set, NULL, &timo);
100
101                 if (!ret)
102                         continue;
103                 if (ret < 0)
104                         break;
105
106                 if (FD_ISSET(p->s, &read_set)) {
107                         ssize_t r = read(p->s, p->buffer, sizeof(p->buffer)-1);
108                         p->buffer_len = r;
109                         p->buffer_done = 0;
110                 //      hdump("pty recv", p->buffer, r);
111                 }
112                 if (p->buffer_done < p->buffer_len) {
113                         // write them in fifo
114                         while (p->buffer_done < p->buffer_len && !uart_pty_fifo_isfull(&p->out))
115                                 uart_pty_fifo_write(&p->out, p->buffer[p->buffer_done++]);
116                 }
117                 if (FD_ISSET(p->s, &write_set)) {
118                         uint8_t buffer[512];
119                         // write them in fifo
120                         uint8_t * dst = buffer;
121                         while (!uart_pty_fifo_isempty(&p->in) && dst < (buffer+sizeof(buffer)))
122                                 *dst++ = uart_pty_fifo_read(&p->in);
123                         size_t len = dst - buffer;
124                         size_t r = write(p->s, buffer, len);
125                 //      hdump("pty send", buffer, r);
126                 }
127         //      uart_pty_flush_incoming(p);
128         }
129         return NULL;
130 }
131
132 static const char * irq_names[IRQ_UART_PTY_COUNT] = {
133         [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
134         [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
135 };
136
137 void uart_pty_init(struct avr_t * avr, uart_pty_t * p)
138 {
139         p->avr = avr;
140         p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
141         avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
142
143         int m, s;
144
145         if (openpty(&m, &s, p->slavename, NULL, NULL) < 0) {
146                 fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
147                 return ;
148         }
149         p->s = m;
150
151         printf("uart_pty_init bridge on port *** %s ***\n", p->slavename);
152
153         pthread_create(&p->thread, NULL, uart_pty_thread, p);
154
155 }
156
157 void uart_pty_stop(uart_pty_t * p)
158 {
159         puts(__func__);
160         pthread_kill(p->thread, SIGINT);
161         close(p->s);
162         void * ret;
163         pthread_join(p->thread, &ret);
164 }
165
166 void uart_pty_connect(uart_pty_t * p, char uart)
167 {
168         // disable the stdio dump, as we are sending binary there
169         uint32_t f = 0;
170         avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
171         f &= ~AVR_UART_FLAG_STDIO;
172         avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
173
174         avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
175         avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
176         avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
177         avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
178         if (src && dst) {
179                 avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
180                 avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
181         }
182         if (xon)
183                 avr_irq_register_notify(xon, uart_pty_xon_hook, p);
184         if (xoff)
185                 avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
186 }
187