diff options
Diffstat (limited to 'aoview/aoview_serial.c')
| -rw-r--r-- | aoview/aoview_serial.c | 270 | 
1 files changed, 270 insertions, 0 deletions
diff --git a/aoview/aoview_serial.c b/aoview/aoview_serial.c new file mode 100644 index 00000000..5cb286f8 --- /dev/null +++ b/aoview/aoview_serial.c @@ -0,0 +1,270 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * 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 <termios.h> + +#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; +	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 (callback && (revents & G_IO_IN)) +		(*callback)(user_data); +	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) { +		free (serial); +		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); +	return serial; +} + +void +aoview_serial_close(struct aoview_serial *serial) +{ +	g_source_remove_poll(&serial->source, &serial->poll_fd); +	g_source_destroy(&serial->source); +	g_source_unref(&serial->source); +} + +void +aoview_serial_set_callback(struct aoview_serial *serial, +			   GSourceFunc func, +			   gpointer data, +			   GDestroyNotify notify) +{ +	g_source_set_callback(&serial->source, func, data, notify); +}  | 
