X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=ao-tools%2Fao-view%2Faoview_serial.c;fp=ao-tools%2Fao-view%2Faoview_serial.c;h=29038b79af6ea747af0da5bd222ed33acb5275ea;hp=0000000000000000000000000000000000000000;hb=0c771d999914f9d17c723900f2987acc45fd0fbb;hpb=15341b6e6dcf52df083d6aa37ef881ea6ad48ee5 diff --git a/ao-tools/ao-view/aoview_serial.c b/ao-tools/ao-view/aoview_serial.c new file mode 100644 index 00000000..29038b79 --- /dev/null +++ b/ao-tools/ao-view/aoview_serial.c @@ -0,0 +1,270 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program 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; version 2 of the License. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "aoview.h" +#include + +#define AOVIEW_SERIAL_IN_BUF 64 +#define AOVIEW_SERIAL_OUT_BUF 64 + +struct aoview_buf { + char *buf; + int off; + int count; + int size; +}; + +static int +aoview_buf_write(struct aoview_buf *buf, char *data, int len) +{ + if (buf->count + len > buf->size) { + int new_size = buf->size * 2; + if (new_size == 0) + new_size = 1024; + if (buf->buf) + buf->buf = realloc (buf->buf, new_size); + else + buf->buf = malloc (new_size); + buf->size = new_size; + } + memcpy(buf->buf + buf->count, data, len); + buf->count += len; + return len; +} + +static int +aoview_buf_read(struct aoview_buf *buf, char *data, int len) +{ + if (len > buf->count - buf->off) + len = buf->count - buf->off; + memcpy (data, buf->buf + buf->off, len); + buf->off += len; + if (buf->off == buf->count) + buf->off = buf->count = 0; + return len; +} + +static int +aoview_buf_getc(struct aoview_buf *buf) +{ + char b; + int r; + + r = aoview_buf_read(buf, &b, 1); + if (r == 1) + return (int) b; + return -1; +} + +static void +aoview_buf_flush(struct aoview_buf *buf, int fd) +{ + int ret; + + if (buf->count > buf->off) { + ret = write(fd, buf->buf + buf->off, buf->count - buf->off); + if (ret > 0) { + buf->off += ret; + if (buf->off == buf->count) + buf->off = buf->count = 0; + } + } +} + +static void +aoview_buf_fill(struct aoview_buf *buf, int fd) +{ + int ret; + + while (buf->count >= buf->size) { + int new_size = buf->size * 2; + buf->buf = realloc (buf->buf, new_size); + buf->size = new_size; + } + + ret = read(fd, buf->buf + buf->count, buf->size - buf->count); + if (ret > 0) + buf->count += ret; +} + +static void +aoview_buf_init(struct aoview_buf *buf) +{ + buf->buf = malloc (buf->size = 1024); + buf->count = 0; +} + +static void +aoview_buf_fini(struct aoview_buf *buf) +{ + free(buf->buf); +} + +struct aoview_serial { + GSource source; + int fd; + struct termios save_termios; + struct aoview_buf in_buf; + struct aoview_buf out_buf; + GPollFD poll_fd; +}; + + +void +aoview_serial_printf(struct aoview_serial *serial, char *format, ...) +{ + char buf[1024]; + va_list ap; + int ret; + + /* sprintf to a local buffer */ + va_start(ap, format); + ret = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + if (ret > sizeof(buf)) { + fprintf(stderr, "printf overflow for format %s\n", + format); + } + + /* flush local buffer to the wire */ + aoview_buf_write(&serial->out_buf, buf, ret); + aoview_buf_flush(&serial->out_buf, serial->fd); +} + +int +aoview_serial_read(struct aoview_serial *serial, char *buf, int len) +{ + return aoview_buf_read(&serial->in_buf, buf, len); +} + +int +aoview_serial_getc(struct aoview_serial *serial) +{ + return aoview_buf_getc(&serial->in_buf); +} + +static gboolean +serial_prepare(GSource *source, gint *timeout) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + *timeout = -1; + + if (serial->out_buf.count) + serial->poll_fd.events |= G_IO_OUT; + else + serial->poll_fd.events &= ~G_IO_OUT; + return FALSE; +} + +static gboolean +serial_check(GSource *source) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + gint revents = serial->poll_fd.revents; + + if (revents & G_IO_NVAL) + return FALSE; + if (revents & G_IO_IN) + return TRUE; + if (revents & G_IO_OUT) + return TRUE; + return FALSE; +} + +static gboolean +serial_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + aoview_serial_callback func = (aoview_serial_callback) callback; + gint revents = serial->poll_fd.revents; + + if (revents & G_IO_IN) + aoview_buf_fill(&serial->in_buf, serial->fd); + + if (revents & G_IO_OUT) + aoview_buf_flush(&serial->out_buf, serial->fd); + + if (func) + (*func)(user_data, serial, revents); + return TRUE; +} + +static void +serial_finalize(GSource *source) +{ + struct aoview_serial *serial = (struct aoview_serial *) source; + + aoview_buf_fini(&serial->in_buf); + aoview_buf_fini(&serial->out_buf); + tcsetattr(serial->fd, TCSAFLUSH, &serial->save_termios); + close (serial->fd); +} + +static GSourceFuncs serial_funcs = { + serial_prepare, + serial_check, + serial_dispatch, + serial_finalize +}; + +struct aoview_serial * +aoview_serial_open(const char *tty) +{ + struct aoview_serial *serial; + struct termios termios; + + serial = (struct aoview_serial *) g_source_new(&serial_funcs, sizeof (struct aoview_serial)); + aoview_buf_init(&serial->in_buf); + aoview_buf_init(&serial->out_buf); + serial->fd = open (tty, O_RDWR | O_NONBLOCK); + if (serial->fd < 0) { + g_source_destroy(&serial->source); + return NULL; + } + tcgetattr(serial->fd, &termios); + serial->save_termios = termios; + cfmakeraw(&termios); + tcsetattr(serial->fd, TCSAFLUSH, &termios); + + aoview_serial_printf(serial, "E 0\n"); + tcdrain(serial->fd); + usleep(15*1000); + tcflush(serial->fd, TCIFLUSH); + serial->poll_fd.fd = serial->fd; + serial->poll_fd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR; + g_source_attach(&serial->source, NULL); + g_source_add_poll(&serial->source,&serial->poll_fd); + aoview_serial_set_callback(serial, NULL); + return serial; +} + +void +aoview_serial_close(struct aoview_serial *serial) +{ + g_source_remove_poll(&serial->source, &serial->poll_fd); + close(serial->fd); + g_source_destroy(&serial->source); +} + +void +aoview_serial_set_callback(struct aoview_serial *serial, + aoview_serial_callback func) +{ + g_source_set_callback(&serial->source, (GSourceFunc) func, serial, NULL); +}