Commit fcfd703f95cc714f7bcc1eede3905b18d98b2b92
authorMichel Pollet <buserror@gmail.com>
Thu, 15 Mar 2012 14:18:01 +0000 (14:18 +0000)
committerMichel Pollet <buserror@gmail.com>
Thu, 15 Mar 2012 14:18:01 +0000 (14:18 +0000)
This one replaces uart_udp, that was really a toy. This new part simulates
a proper serial port (almost) on the host, and allow connection with minicom
or anything you like.
Properly implemented flow control too, do it's safe at any speed.

Signed-off-by: Michel Pollet <buserror@gmail.com>
2 files changed:
examples/parts/uart_pty.c [new file with mode: 0644]
examples/parts/uart_pty.h [new file with mode: 0644]

diff --git a/examples/parts/uart_pty.c b/examples/parts/uart_pty.c
new file mode 100644 (file)
index 0000000..b442825
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+       uart_pty.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/select.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pty.h>
+#include <signal.h>
+
+#include "uart_pty.h"
+#include "avr_uart.h"
+#include "sim_hex.h"
+
+DEFINE_FIFO(uint8_t,uart_pty_fifo);
+
+/*
+ * called when a byte is send via the uart on the AVR
+ */
+static void uart_pty_in_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       //printf("uart_pty_in_hook %02x\n", value);
+       uart_pty_fifo_write(&p->in, value);
+}
+
+// try to empty our fifo, the uart_pty_xoff_hook() will be called when
+// other side is full
+static void  uart_pty_flush_incoming(uart_pty_t * p)
+{
+       while (p->xon && !uart_pty_fifo_isempty(&p->out)) {
+               uint8_t byte = uart_pty_fifo_read(&p->out);
+       //      printf("uart_pty_flush_incoming send %02x\n", byte);
+               avr_raise_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, byte);
+       }
+}
+
+/*
+ * Called when the uart has room in it's input buffer. This is called repeateadly
+ * if necessary, while the xoff is called only when the uart fifo is FULL
+ */
+static void uart_pty_xon_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       if (!p->xon)
+               printf("uart_pty_xon_hook\n");
+       p->xon = 1;
+       uart_pty_flush_incoming(p);
+}
+
+/*
+ * Called when the uart ran out of room in it's input buffer
+ */
+static void uart_pty_xoff_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+       if (p->xon)
+               printf("uart_pty_xoff_hook\n");
+       p->xon = 0;
+}
+
+static void * uart_pty_thread(void * param)
+{
+       uart_pty_t * p = (uart_pty_t*)param;
+
+       while (1) {
+               fd_set read_set, write_set;
+               int max = p->s + 1;
+               FD_ZERO(&read_set);
+               FD_ZERO(&write_set);
+
+               // read more only if buffer was flushed
+               if (p->buffer_len == p->buffer_done)
+                       FD_SET(p->s, &read_set);
+               if (!uart_pty_fifo_isempty(&p->in))
+                       FD_SET(p->s, &write_set);
+
+               struct timeval timo = { 0, 500 };       // short, but not too short interval
+               int ret = select(max, &read_set, &write_set, NULL, &timo);
+
+               if (!ret)
+                       continue;
+               if (ret < 0)
+                       break;
+
+               if (FD_ISSET(p->s, &read_set)) {
+                       ssize_t r = read(p->s, p->buffer, sizeof(p->buffer)-1);
+                       p->buffer_len = r;
+                       p->buffer_done = 0;
+               //      hdump("pty recv", p->buffer, r);
+               }
+               if (p->buffer_done < p->buffer_len) {
+                       // write them in fifo
+                       while (p->buffer_done < p->buffer_len && !uart_pty_fifo_isfull(&p->out))
+                               uart_pty_fifo_write(&p->out, p->buffer[p->buffer_done++]);
+               }
+               if (FD_ISSET(p->s, &write_set)) {
+                       uint8_t buffer[512];
+                       // write them in fifo
+                       uint8_t * dst = buffer;
+                       while (!uart_pty_fifo_isempty(&p->in) && dst < (buffer+sizeof(buffer)))
+                               *dst++ = uart_pty_fifo_read(&p->in);
+                       size_t len = dst - buffer;
+                       size_t r = write(p->s, buffer, len);
+               //      hdump("pty send", buffer, r);
+               }
+       //      uart_pty_flush_incoming(p);
+       }
+       return NULL;
+}
+
+static const char * irq_names[IRQ_UART_PTY_COUNT] = {
+       [IRQ_UART_PTY_BYTE_IN] = "8<uart_pty.in",
+       [IRQ_UART_PTY_BYTE_OUT] = "8>uart_pty.out",
+};
+
+void uart_pty_init(struct avr_t * avr, uart_pty_t * p)
+{
+       p->avr = avr;
+       p->irq = avr_alloc_irq(&avr->irq_pool, 0, IRQ_UART_PTY_COUNT, irq_names);
+       avr_irq_register_notify(p->irq + IRQ_UART_PTY_BYTE_IN, uart_pty_in_hook, p);
+
+       int m, s;
+
+       if (openpty(&m, &s, p->slavename, NULL, NULL) < 0) {
+               fprintf(stderr, "%s: Can't create pty: %s", __FUNCTION__, strerror(errno));
+               return ;
+       }
+       p->s = m;
+
+       printf("uart_pty_init bridge on port *** %s ***\n", p->slavename);
+
+       pthread_create(&p->thread, NULL, uart_pty_thread, p);
+
+}
+
+void uart_pty_stop(uart_pty_t * p)
+{
+       puts(__func__);
+       pthread_kill(p->thread, SIGINT);
+       close(p->s);
+       void * ret;
+       pthread_join(p->thread, &ret);
+}
+
+void uart_pty_connect(uart_pty_t * p, char uart)
+{
+       // disable the stdio dump, as we are sending binary there
+       uint32_t f = 0;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_GET_FLAGS(uart), &f);
+       f &= ~AVR_UART_FLAG_STDIO;
+       avr_ioctl(p->avr, AVR_IOCTL_UART_SET_FLAGS(uart), &f);
+
+       avr_irq_t * src = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUTPUT);
+       avr_irq_t * dst = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_INPUT);
+       avr_irq_t * xon = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XON);
+       avr_irq_t * xoff = avr_io_getirq(p->avr, AVR_IOCTL_UART_GETIRQ(uart), UART_IRQ_OUT_XOFF);
+       if (src && dst) {
+               avr_connect_irq(src, p->irq + IRQ_UART_PTY_BYTE_IN);
+               avr_connect_irq(p->irq + IRQ_UART_PTY_BYTE_OUT, dst);
+       }
+       if (xon)
+               avr_irq_register_notify(xon, uart_pty_xon_hook, p);
+       if (xoff)
+               avr_irq_register_notify(xoff, uart_pty_xoff_hook, p);
+}
+
diff --git a/examples/parts/uart_pty.h b/examples/parts/uart_pty.h
new file mode 100644 (file)
index 0000000..41a402f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+       uart_pty.h
+
+       Copyright 2012 Michel Pollet <buserror@gmail.com>
+
+       This file is part of simavr.
+
+       simavr is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 3 of the License, or
+       (at your option) any later version.
+
+       simavr is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __UART_PTY_H___
+#define __UART_PTY_H___
+
+#include "sim_irq.h"
+#include "fifo_declare.h"
+
+enum {
+       IRQ_UART_PTY_BYTE_IN = 0,
+       IRQ_UART_PTY_BYTE_OUT,
+       IRQ_UART_PTY_COUNT
+};
+
+DECLARE_FIFO(uint8_t,uart_pty_fifo, 512);
+
+typedef struct uart_pty_t {
+       avr_irq_t *     irq;            // irq list
+       struct avr_t *avr;              // keep it around so we can pause it
+
+       pthread_t       thread;
+       int             s;                      // socket we chat on
+       char            slavename[64];
+       int                     xon;
+       uart_pty_fifo_t in;
+       uart_pty_fifo_t out;
+
+       uint8_t         buffer[512];
+       size_t          buffer_len, buffer_done;
+} uart_pty_t;
+
+void uart_pty_init(struct avr_t * avr, uart_pty_t * b);
+void uart_pty_stop(uart_pty_t * p);
+
+void uart_pty_connect(uart_pty_t * p, char uart);
+
+#endif /* __UART_PTY_H___ */