From: Michel Pollet Date: Thu, 15 Mar 2012 14:18:01 +0000 (+0000) Subject: uart_pty: New part X-Git-Tag: v1.0b1~20 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=fcfd703f95cc714f7bcc1eede3905b18d98b2b92;p=sx%2Fsimavr.git uart_pty: New part 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 --- diff --git a/examples/parts/uart_pty.c b/examples/parts/uart_pty.c new file mode 100644 index 0000000..b442825 --- /dev/null +++ b/examples/parts/uart_pty.c @@ -0,0 +1,187 @@ +/* + uart_pty.c + + Copyright 2008, 2009 Michel Pollet + + 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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] = "8avr = 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 index 0000000..41a402f --- /dev/null +++ b/examples/parts/uart_pty.h @@ -0,0 +1,57 @@ +/* + uart_pty.h + + Copyright 2012 Michel Pollet + + 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 . + */ + + +#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___ */