ao-tools: Add ao-sky-flash to update GPS firmware
[fw/altos] / ao-tools / ao-sky-flash / sky_serial.c
1 /*
2  * Copyright © 2012 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 #define _BSD_SOURCE
19 #include <termios.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <poll.h>
25 #include "sky_flash.h"
26 #include <stdio.h>
27 #include <sys/time.h>
28 #include <stdint.h>
29 #include <unistd.h>
30 #include <stdarg.h>
31
32 int     skytraq_verbose = 1;
33
34 int
35 skytraq_setspeed(int fd, int baud)
36 {
37         int     b;
38         int     ret;
39         struct termios  term;
40
41         switch (baud) {
42         case 9600:
43                 b = B9600;
44                 break;
45         case 38400:
46                 b = B38400;
47                 break;
48         case 115200:
49                 b = B115200;
50                 break;
51         default:
52                 fprintf (stderr, "Invalid baudrate %d\n", baud);
53                 return -1;
54         }
55         ret = tcgetattr(fd, &term);
56         cfmakeraw(&term);
57 #ifdef USE_POLL
58         term.c_cc[VMIN] = 1;
59         term.c_cc[VTIME] = 0;
60 #else
61         term.c_cc[VMIN] = 0;
62         term.c_cc[VTIME] = 1;
63 #endif
64
65         cfsetspeed(&term, b);
66
67         ret = tcsetattr(fd, TCSAFLUSH, &term);
68         return ret;
69 }
70
71 int     skytraq_open_time;
72
73 int
74 skytraq_open(const char *path)
75 {
76         int             fd;
77         int             ret;
78
79         fd = open(path, O_RDWR | O_NOCTTY);
80         if (fd < 0) {
81                 perror (path);
82                 return -1;
83         }
84
85         ret = skytraq_setspeed(fd, 9600);
86         if (ret < 0) {
87                 close (fd);
88                 return -1;
89         }
90         skytraq_open_time = skytraq_millis();
91         return fd;
92 }
93
94
95 #define BAUD            57600
96 #define BPS             (BAUD/10 * 9/10)
97 #define US_PER_CHAR     (1000000 / BPS)
98
99 int
100 skytraq_write(int fd, const char *data, int len)
101 {
102         const char *d = data;
103         int             r;
104         int             us;
105
106         while (len) {
107                 int     this_time = len;
108                 if (this_time > 128)
109                         this_time = 128;
110                 if (this_time < 70) {
111                         skytraq_dbg_printf (0, "%4d: ", this_time);
112                         skytraq_dbg_buf(0, data, this_time);
113                 } else {
114                         skytraq_dbg_printf (0, "%d bytes (%d remain)", this_time, len);
115                 }
116                 fflush(stdout);
117                 r = write(fd, data, this_time);
118                 if (r <= 0)
119                         return r;
120                 us = r * US_PER_CHAR;
121                 usleep(r * US_PER_CHAR);
122                 data += r;
123                 len -= r;
124         }
125         skytraq_dbg_newline();
126         return 1;
127 }
128
129 int
130 skytraq_setcomm(int fd, int baudrate)
131 {
132         uint8_t msg[11];
133         int     i;
134         uint8_t cksum;
135
136         int target_baudrate;
137         switch(baudrate)
138         {
139         case 4800:
140                 target_baudrate=0;
141                 break;
142         case 9600:
143                 target_baudrate=1;
144                 break;
145         case 19200:
146                 target_baudrate=2;
147                 break;
148         case 38400:
149                 target_baudrate=3;
150                 break;
151         case 57600:
152                 target_baudrate=4;
153                 break;
154         case 115200:
155                 target_baudrate=5;
156                 break;
157         case 230400:
158                 target_baudrate=6;
159                 break;
160         }
161         msg[0] = 0xa0;  /* header */
162         msg[1] = 0xa1;
163         msg[2] = 0x00;  /* length */
164         msg[3] = 0x04;
165         msg[4] = 0x05;  /* configure serial port */
166         msg[5] = 0x00;  /* COM 1 */
167         msg[6] = target_baudrate;
168         msg[7] = 0x00;  /* update to SRAM only */
169
170         cksum = 0;
171         for (i = 4; i < 8; i++)
172                 cksum ^= msg[i];
173         msg[8] = cksum;
174         msg[9] = 0x0d;
175         msg[10] = 0x0a;
176         return skytraq_write(fd, msg, 11);
177 }
178
179 int
180 skytraq_waitchar(int fd, int timeout)
181 {
182         struct pollfd   fds[1];
183         int             ret;
184         unsigned char   c;
185
186         for (;;) {
187                 fds[0].fd = fd;
188                 fds[0].events = POLLIN;
189                 ret = poll(fds, 1, timeout);
190                 if (ret >= 1) {
191                         if (fds[0].revents & POLLIN) {
192                                 ret = read(fd, &c, 1);
193                                 if (ret == 1) {
194                                         skytraq_dbg_char(1, c);
195                                         return c;
196                                 }
197                         }
198                 } else if (ret == 0)
199                         return -2;
200                 else {
201                         perror("poll");
202                         return -1;
203                 }
204         }
205 }
206
207 int
208 skytraq_waitstatus(int fd, const char *status, int timeout)
209 {
210         const char      *s;
211         int             c;
212
213         for (;;) {
214                 c = skytraq_waitchar(fd, timeout);
215                 if (c < 0) {
216                         skytraq_dbg_newline();
217                         return c;
218                 }
219                 if ((char) c == *status) {
220                         s = status + 1;
221                         for (;;) {
222                                 c = skytraq_waitchar(fd, timeout);
223                                 if (c < 0) {
224                                         skytraq_dbg_newline();
225                                         return c;
226                                 }
227                                 if ((char) c != *s)
228                                         break;
229                                 if (!*s) {
230                                         skytraq_dbg_newline();
231                                         return 0;
232                                 }
233                                 s++;
234                         }
235                 }
236         }
237 }
238
239 void
240 skytraq_flush(int fd)
241 {
242         while (skytraq_waitchar(fd, 1) >= 0)
243                 ;
244 }
245
246 int
247 skytraq_cmd_wait(int fd, const char *message, int len, const char *status, int timeout)
248 {
249         skytraq_flush(fd);
250         skytraq_write(fd, message, len);
251         return skytraq_waitstatus(fd, status, timeout);
252 }
253
254 int
255 skytraq_cmd_nowait(int fd, const char *message, int len)
256 {
257         skytraq_flush(fd);
258         return skytraq_write(fd, message, len);
259 }