]> git.gag.com Git - fw/altos/blob - ao-tools/ao-view/aoview_serial.c
altosui: Correct megametrum eeprom filename date
[fw/altos] / ao-tools / ao-view / 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         aoview_serial_callback func = (aoview_serial_callback) callback;
195         gint revents = serial->poll_fd.revents;
196
197         if (revents & G_IO_IN)
198                 aoview_buf_fill(&serial->in_buf, serial->fd);
199
200         if (revents & G_IO_OUT)
201                 aoview_buf_flush(&serial->out_buf, serial->fd);
202
203         if (func)
204                 (*func)(user_data, serial, revents);
205         return TRUE;
206 }
207
208 static void
209 serial_finalize(GSource *source)
210 {
211         struct aoview_serial *serial = (struct aoview_serial *) source;
212
213         aoview_buf_fini(&serial->in_buf);
214         aoview_buf_fini(&serial->out_buf);
215         tcsetattr(serial->fd, TCSAFLUSH, &serial->save_termios);
216         close (serial->fd);
217 }
218
219 static GSourceFuncs serial_funcs = {
220         serial_prepare,
221         serial_check,
222         serial_dispatch,
223         serial_finalize
224 };
225
226 struct aoview_serial *
227 aoview_serial_open(const char *tty)
228 {
229         struct aoview_serial    *serial;
230         struct termios  termios;
231
232         serial = (struct aoview_serial *) g_source_new(&serial_funcs, sizeof (struct aoview_serial));
233         aoview_buf_init(&serial->in_buf);
234         aoview_buf_init(&serial->out_buf);
235         serial->fd = open (tty, O_RDWR | O_NONBLOCK);
236         if (serial->fd < 0) {
237                 g_source_destroy(&serial->source);
238                 return NULL;
239         }
240         tcgetattr(serial->fd, &termios);
241         serial->save_termios = termios;
242         cfmakeraw(&termios);
243         tcsetattr(serial->fd, TCSAFLUSH, &termios);
244
245         aoview_serial_printf(serial, "E 0\n");
246         tcdrain(serial->fd);
247         usleep(15*1000);
248         tcflush(serial->fd, TCIFLUSH);
249         serial->poll_fd.fd = serial->fd;
250         serial->poll_fd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
251         g_source_attach(&serial->source, NULL);
252         g_source_add_poll(&serial->source,&serial->poll_fd);
253         aoview_serial_set_callback(serial, NULL);
254         return serial;
255 }
256
257 void
258 aoview_serial_close(struct aoview_serial *serial)
259 {
260         g_source_remove_poll(&serial->source, &serial->poll_fd);
261         close(serial->fd);
262         g_source_destroy(&serial->source);
263 }
264
265 void
266 aoview_serial_set_callback(struct aoview_serial *serial,
267                            aoview_serial_callback func)
268 {
269         g_source_set_callback(&serial->source, (GSourceFunc) func, serial, NULL);
270 }