Add preliminary aoview code
[fw/altos] / aoview / aoview_serial.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "aoview.h"
19 #include <termios.h>
20
21 #define AOVIEW_SERIAL_IN_BUF    64
22 #define AOVIEW_SERIAL_OUT_BUF   64
23
24 struct aoview_buf {
25         char            *buf;
26         int             off;
27         int             count;
28         int             size;
29 };
30
31 static int
32 aoview_buf_write(struct aoview_buf *buf, char *data, int len)
33 {
34         if (buf->count + len > buf->size) {
35                 int     new_size = buf->size * 2;
36                 if (new_size == 0)
37                         new_size = 1024;
38                 if (buf->buf)
39                         buf->buf = realloc (buf->buf, new_size);
40                 else
41                         buf->buf = malloc (new_size);
42                 buf->size = new_size;
43         }
44         memcpy(buf->buf + buf->count, data, len);
45         buf->count += len;
46         return len;
47 }
48
49 static int
50 aoview_buf_read(struct aoview_buf *buf, char *data, int len)
51 {
52         if (len > buf->count - buf->off)
53                 len = buf->count - buf->off;
54         memcpy (data, buf->buf + buf->off, len);
55         buf->off += len;
56         if (buf->off == buf->count)
57                 buf->off = buf->count = 0;
58         return len;
59 }
60
61 static int
62 aoview_buf_getc(struct aoview_buf *buf)
63 {
64         char    b;
65         int     r;
66
67         r = aoview_buf_read(buf, &b, 1);
68         if (r == 1)
69                 return (int) b;
70         return -1;
71 }
72
73 static void
74 aoview_buf_flush(struct aoview_buf *buf, int fd)
75 {
76         int     ret;
77
78         if (buf->count > buf->off) {
79                 ret = write(fd, buf->buf + buf->off, buf->count - buf->off);
80                 if (ret > 0) {
81                         buf->off += ret;
82                         if (buf->off == buf->count)
83                                 buf->off = buf->count = 0;
84                 }
85         }
86 }
87
88 static void
89 aoview_buf_fill(struct aoview_buf *buf, int fd)
90 {
91         int ret;
92
93         while (buf->count >= buf->size) {
94                 int new_size = buf->size * 2;
95                 buf->buf = realloc (buf->buf, new_size);
96                 buf->size = new_size;
97         }
98
99         ret = read(fd, buf->buf + buf->count, buf->size - buf->count);
100         if (ret > 0)
101                 buf->count += ret;
102 }
103
104 static void
105 aoview_buf_init(struct aoview_buf *buf)
106 {
107         buf->buf = malloc (buf->size = 1024);
108         buf->count = 0;
109 }
110
111 static void
112 aoview_buf_fini(struct aoview_buf *buf)
113 {
114         free(buf->buf);
115 }
116
117 struct aoview_serial {
118         GSource                 source;
119         int                     fd;
120         struct termios          save_termios;
121         struct aoview_buf       in_buf;
122         struct aoview_buf       out_buf;
123         GPollFD                 poll_fd;
124 };
125
126
127 void
128 aoview_serial_printf(struct aoview_serial *serial, char *format, ...)
129 {
130         char    buf[1024];
131         va_list ap;
132         int     ret;
133
134         /* sprintf to a local buffer */
135         va_start(ap, format);
136         ret = vsnprintf(buf, sizeof(buf), format, ap);
137         va_end(ap);
138         if (ret > sizeof(buf)) {
139                 fprintf(stderr, "printf overflow for format %s\n",
140                         format);
141         }
142
143         /* flush local buffer to the wire */
144         aoview_buf_write(&serial->out_buf, buf, ret);
145         aoview_buf_flush(&serial->out_buf, serial->fd);
146 }
147
148 int
149 aoview_serial_read(struct aoview_serial *serial, char *buf, int len)
150 {
151         return aoview_buf_read(&serial->in_buf, buf, len);
152 }
153
154 int
155 aoview_serial_getc(struct aoview_serial *serial)
156 {
157         return aoview_buf_getc(&serial->in_buf);
158 }
159
160 static gboolean
161 serial_prepare(GSource *source, gint *timeout)
162 {
163         struct aoview_serial *serial = (struct aoview_serial *) source;
164         *timeout = -1;
165
166         if (serial->out_buf.count)
167                 serial->poll_fd.events |= G_IO_OUT;
168         else
169                 serial->poll_fd.events &= ~G_IO_OUT;
170         return FALSE;
171 }
172
173 static gboolean
174 serial_check(GSource *source)
175 {
176         struct aoview_serial *serial = (struct aoview_serial *) source;
177         gint revents = serial->poll_fd.revents;
178
179         if (revents & G_IO_NVAL)
180                 return FALSE;
181         if (revents & G_IO_IN)
182                 return TRUE;
183         if (revents & G_IO_OUT)
184                 return TRUE;
185         return FALSE;
186 }
187
188 static gboolean
189 serial_dispatch(GSource *source,
190                 GSourceFunc callback,
191                 gpointer user_data)
192 {
193         struct aoview_serial *serial = (struct aoview_serial *) source;
194         gint revents = serial->poll_fd.revents;
195
196         if (revents & G_IO_IN)
197                 aoview_buf_fill(&serial->in_buf, serial->fd);
198
199         if (revents & G_IO_OUT)
200                 aoview_buf_flush(&serial->out_buf, serial->fd);
201
202         if (callback && (revents & G_IO_IN))
203                 (*callback)(user_data);
204         return TRUE;
205 }
206
207 static void
208 serial_finalize(GSource *source)
209 {
210         struct aoview_serial *serial = (struct aoview_serial *) source;
211
212         aoview_buf_fini(&serial->in_buf);
213         aoview_buf_fini(&serial->out_buf);
214         tcsetattr(serial->fd, TCSAFLUSH, &serial->save_termios);
215         close (serial->fd);
216 }
217
218 static GSourceFuncs serial_funcs = {
219         serial_prepare,
220         serial_check,
221         serial_dispatch,
222         serial_finalize
223 };
224
225 struct aoview_serial *
226 aoview_serial_open(const char *tty)
227 {
228         struct aoview_serial    *serial;
229         struct termios  termios;
230
231         serial = (struct aoview_serial *) g_source_new(&serial_funcs, sizeof (struct aoview_serial));
232         aoview_buf_init(&serial->in_buf);
233         aoview_buf_init(&serial->out_buf);
234         serial->fd = open (tty, O_RDWR | O_NONBLOCK);
235         if (serial->fd < 0) {
236                 free (serial);
237                 return NULL;
238         }
239         tcgetattr(serial->fd, &termios);
240         serial->save_termios = termios;
241         cfmakeraw(&termios);
242         tcsetattr(serial->fd, TCSAFLUSH, &termios);
243
244         aoview_serial_printf(serial, "E 0\n");
245         tcdrain(serial->fd);
246         usleep(15*1000);
247         tcflush(serial->fd, TCIFLUSH);
248         serial->poll_fd.fd = serial->fd;
249         serial->poll_fd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
250         g_source_attach(&serial->source, NULL);
251         g_source_add_poll(&serial->source,&serial->poll_fd);
252         return serial;
253 }
254
255 void
256 aoview_serial_close(struct aoview_serial *serial)
257 {
258         g_source_remove_poll(&serial->source, &serial->poll_fd);
259         g_source_destroy(&serial->source);
260         g_source_unref(&serial->source);
261 }
262
263 void
264 aoview_serial_set_callback(struct aoview_serial *serial,
265                            GSourceFunc func,
266                            gpointer data,
267                            GDestroyNotify notify)
268 {
269         g_source_set_callback(&serial->source, func, data, notify);
270 }