]> git.gag.com Git - fw/altos/blob - ao-tools/lib/cc-usb.c
altosui/altosdroid: Change message in forbidden map tiles
[fw/altos] / ao-tools / lib / cc-usb.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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <stdarg.h>
23 #include <poll.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <termios.h>
29 #include "ccdbg-debug.h"
30 #include "cc-usb.h"
31
32
33 #define CC_NUM_HEX_READ         64
34 /*
35  * AltOS has different buffer sizes for in/out packets
36  */
37 #define CC_IN_BUF               65536
38 #define CC_OUT_BUF              64
39 #define DEFAULT_TTY             "/dev/ttyACM0"
40
41 struct cc_hex_read {
42         uint8_t *buf;
43         int     len;
44 };
45
46 struct cc_usb {
47         int                     fd;
48         uint8_t                 in_buf[CC_IN_BUF];
49         int                     in_pos;
50         int                     in_count;
51         uint8_t                 out_buf[CC_OUT_BUF];
52         int                     out_count;
53
54         struct cc_hex_read      hex_buf[CC_NUM_HEX_READ];
55         int                     hex_count;
56         int                     show_input;
57
58         int                     remote;
59 };
60
61 #define NOT_HEX 0xff
62
63 static uint8_t
64 cc_hex_nibble(uint8_t c)
65 {
66         if ('0' <= c && c <= '9')
67                 return c - '0';
68         if ('a' <= c && c <= 'f')
69                 return c - 'a' + 10;
70         if ('A' <= c && c <= 'F')
71                 return c - 'A' + 10;
72         return NOT_HEX;
73 }
74
75 /*
76  * Take raw input bytes, parse them as hex
77  * and write them to the waiting buffer
78  */
79 static void
80 cc_handle_hex_read(struct cc_usb *cc)
81 {
82         uint8_t h, l;
83         int     hex_pos;
84
85         hex_pos = 0;
86         while (hex_pos < cc->hex_count && cc->in_pos < cc->in_count) {
87                 /*
88                  * Skip to next hex character
89                  */
90                 while (cc->in_pos < cc->in_count &&
91                        cc_hex_nibble(cc->in_buf[cc->in_pos]) == NOT_HEX)
92                         cc->in_pos++;
93                 /*
94                  * Make sure we have two characters left
95                  */
96                 if (cc->in_count - cc->in_pos < 2)
97                         break;
98                 /*
99                  * Parse hex number
100                  */
101                 h = cc_hex_nibble(cc->in_buf[cc->in_pos]);
102                 l = cc_hex_nibble(cc->in_buf[cc->in_pos+1]);
103                 if (h == NOT_HEX || l == NOT_HEX) {
104                         fprintf(stderr, "hex read error\n");
105                         break;
106                 }
107                 cc->in_pos += 2;
108                 /*
109                  * Store hex number
110                  */
111                 *cc->hex_buf[hex_pos].buf++ = (h << 4) | l;
112                 if (--cc->hex_buf[hex_pos].len <= 0)
113                         hex_pos++;
114         }
115
116         /* Move pending hex reads to the start of the array */
117         if (hex_pos) {
118                 memmove(cc->hex_buf, cc->hex_buf + hex_pos,
119                         (cc->hex_count - hex_pos) * sizeof (cc->hex_buf[0]));
120                 cc->hex_count -= hex_pos;
121         }
122 }
123
124 static void
125 cc_usb_dbg(int indent, uint8_t *bytes, int len)
126 {
127         static int      eol = 1;
128         int     i;
129         uint8_t c;
130         ccdbg_debug(CC_DEBUG_BITBANG, "<<<%d bytes>>>", len);
131         while (len--) {
132                 c = *bytes++;
133                 if (eol) {
134                         for (i = 0; i < indent; i++)
135                                 ccdbg_debug(CC_DEBUG_BITBANG, " ");
136                         eol = 0;
137                 }
138                 switch (c) {
139                 case '\r':
140                         ccdbg_debug(CC_DEBUG_BITBANG, "\\r");
141                         break;
142                 case '\n':
143                         eol = 1;
144                         ccdbg_debug(CC_DEBUG_BITBANG, "\\n\n");
145                         break;
146                 default:
147                         if (c < ' ' || c > '~')
148                                 ccdbg_debug(CC_DEBUG_BITBANG, "\\%02x", c);
149                         else
150                                 ccdbg_debug(CC_DEBUG_BITBANG, "%c", c);
151                 }
152         }
153 }
154
155 int     cc_default_timeout = 5000;
156
157 /*
158  * Flush pending writes, fill pending reads
159  */
160
161 static int
162 _cc_usb_sync(struct cc_usb *cc, int wait_for_input, int write_timeout)
163 {
164         int             ret;
165         struct pollfd   fds;
166         int             timeout;
167
168         fds.fd = cc->fd;
169         for (;;) {
170                 if (cc->hex_count || cc->out_count)
171                         timeout = write_timeout;
172                 else if (wait_for_input && cc->in_pos == cc->in_count)
173                         timeout = wait_for_input;
174                 else
175                         timeout = 0;
176                 fds.events = 0;
177                 /* Move remaining bytes to the start of the input buffer */
178                 if (cc->in_pos) {
179                         memmove(cc->in_buf, cc->in_buf + cc->in_pos,
180                                 cc->in_count - cc->in_pos);
181                         cc->in_count -= cc->in_pos;
182                         cc->in_pos = 0;
183                 }
184                 if (cc->in_count < CC_IN_BUF)
185                         fds.events |= POLLIN;
186                 if (cc->out_count)
187                         fds.events |= POLLOUT;
188                 ret = poll(&fds, 1, timeout);
189                 if (ret == 0) {
190                         if (timeout)
191                                 return -1;
192                         break;
193                 }
194                 if (ret < 0) {
195                         perror("poll");
196                         return -1;
197                 }
198                 if (fds.revents & POLLIN) {
199                         ret = read(cc->fd, cc->in_buf + cc->in_count,
200                                    CC_IN_BUF - cc->in_count);
201                         if (ret > 0) {
202                                 cc_usb_dbg(24, cc->in_buf + cc->in_count, ret);
203                                 cc->in_count += ret;
204                                 if (cc->hex_count)
205                                         cc_handle_hex_read(cc);
206                                 if (cc->show_input && cc->in_count) {
207                                         write(2, cc->in_buf, cc->in_count);
208                                         cc->in_count = 0;
209                                 }
210                         } else if (ret <= 0) {
211                                 perror("read");
212                                 return -1;
213                         }
214                 }
215                 if (fds.revents & POLLOUT) {
216                         ret = write(cc->fd, cc->out_buf,
217                                     cc->out_count);
218                         if (ret > 0) {
219                                 cc_usb_dbg(0, cc->out_buf, ret);
220                                 memmove(cc->out_buf,
221                                         cc->out_buf + ret,
222                                         cc->out_count - ret);
223                                 cc->out_count -= ret;
224                         } else if (ret < 0)
225                                 perror("write");
226                 }
227         }
228         return 0;
229 }
230
231 void
232 cc_usb_sync(struct cc_usb *cc)
233 {
234         if (_cc_usb_sync(cc, 0, cc_default_timeout) < 0) {
235                 fprintf(stderr, "USB link timeout\n");
236                 exit(1);
237         }
238 }
239
240 void
241 cc_usb_printf(struct cc_usb *cc, char *format, ...)
242 {
243         char    buf[1024], *b;
244         va_list ap;
245         int     ret, this_time;
246
247         /* sprintf to a local buffer */
248         va_start(ap, format);
249         ret = vsnprintf(buf, sizeof(buf), format, ap);
250         va_end(ap);
251         if (ret > sizeof(buf)) {
252                 fprintf(stderr, "printf overflow for format %s\n",
253                         format);
254         }
255
256         /* flush local buffer to the wire */
257         b = buf;
258         while (ret > 0) {
259                 this_time = ret;
260                 if (this_time > CC_OUT_BUF - cc->out_count)
261                         this_time = CC_OUT_BUF - cc->out_count;
262                 memcpy(cc->out_buf + cc->out_count, b, this_time);
263                 cc->out_count += this_time;
264                 ret -= this_time;
265                 b += this_time;
266                 while (cc->out_count >= CC_OUT_BUF)
267                         cc_usb_sync(cc);
268         }
269 }
270
271 int
272 cc_usb_getchar_timeout(struct cc_usb *cc, int timeout)
273 {
274         while (cc->in_pos == cc->in_count) {
275                 if (_cc_usb_sync(cc, timeout, cc_default_timeout) < 0) {
276                         fprintf(stderr, "USB link timeout\n");
277                         exit(1);
278                 }
279         }
280         return cc->in_buf[cc->in_pos++];
281 }
282
283 int
284 cc_usb_getchar(struct cc_usb *cc)
285 {
286         return cc_usb_getchar_timeout(cc, cc_default_timeout);
287 }
288
289 void
290 cc_usb_getline(struct cc_usb *cc, char *line, int max)
291 {
292         int     c;
293
294         while ((c = cc_usb_getchar(cc)) != '\n') {
295                 switch (c) {
296                 case '\r':
297                         break;
298                 default:
299                         if (max > 1) {
300                                 *line++ = c;
301                                 max--;
302                         }
303                         break;
304                 }
305         }
306         *line++ = '\0';
307 }
308
309 int
310 cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len)
311 {
312         int     this_len;
313         int     ret = len;
314
315         while (len) {
316                 this_len = len;
317                 if (this_len > 8)
318                         this_len = 8;
319                 len -= this_len;
320                 cc_usb_printf(cc, "P");
321                 while (this_len--)
322                         cc_usb_printf (cc, " %02x", (*bytes++) & 0xff);
323                 cc_usb_printf(cc, "\n");
324         }
325         return ret;
326 }
327
328 void
329 cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len)
330 {
331         struct cc_hex_read      *hex_buf;
332
333         /* At the start of a command sequence, flush any pending input */
334         if (cc->hex_count == 0) {
335                 cc_usb_sync(cc);
336                 cc->in_count = 0;
337         }
338         while (cc->hex_count >= CC_NUM_HEX_READ)
339                 cc_usb_sync(cc);
340         hex_buf = &cc->hex_buf[cc->hex_count++];
341         hex_buf->buf = buf;
342         hex_buf->len = len;
343 }
344
345 int
346 cc_usb_recv_bytes(struct cc_usb *cc, uint8_t *buf, int len)
347 {
348         cc_queue_read(cc, buf, len);
349         cc_usb_printf(cc, "G %x\n", len);
350         return len;
351 }
352
353 int
354 cc_usb_write_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
355 {
356         cc_usb_printf(cc, "O %x %x\n", len, addr);
357         while (len--)
358                 cc_usb_printf(cc, "%02x", *bytes++);
359         cc_usb_sync(cc);
360         return 0;
361 }
362
363 int
364 cc_usb_read_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
365 {
366         int     i;
367         cc_queue_read(cc, bytes, len);
368         cc_usb_printf(cc, "I %x %x\n", len, addr);
369         cc_usb_sync(cc);
370         for (i = 0; i < len; i++) {
371                 if ((i & 15) == 0) {
372                         if (i)
373                                 ccdbg_debug(CC_DEBUG_MEMORY, "\n");
374                         ccdbg_debug(CC_DEBUG_MEMORY, "\t%04x", addr + i);
375                 }
376                 ccdbg_debug(CC_DEBUG_MEMORY, " %02x", bytes[i]);
377         }
378         ccdbg_debug(CC_DEBUG_MEMORY, "\n");
379         return 0;
380 }
381
382 int
383 cc_usb_debug_mode(struct cc_usb *cc)
384 {
385         cc_usb_sync(cc);
386         cc_usb_printf(cc, "D\n");
387         return 1;
388 }
389
390 int
391 cc_usb_reset(struct cc_usb *cc)
392 {
393         cc_usb_sync(cc);
394         cc_usb_printf(cc, "R\n");
395         return 1;
396 }
397
398 void
399 cc_usb_open_remote(struct cc_usb *cc, int freq, char *call)
400 {
401         if (!cc->remote) {
402                 fprintf (stderr, "freq %dkHz\n", freq);
403                 fprintf (stderr, "call %s\n", call);
404                 cc_usb_printf(cc, "\nc F %d\nc c %s\np\nE 0\n", freq, call);
405                 do {
406                         cc->in_count = cc->in_pos = 0;
407                         _cc_usb_sync(cc, 100, cc_default_timeout);
408                 } while (cc->in_count > 0);
409                 cc->remote = 1;
410         }
411 }
412
413 void
414 cc_usb_close_remote(struct cc_usb *cc)
415 {
416         if (cc->remote) {
417                 cc_usb_printf(cc, "~");
418                 cc->remote = 0;
419         }
420 }
421
422 static struct termios   save_termios;
423
424 #include <errno.h>
425
426 struct cc_usb *
427 cc_usb_open(char *tty)
428 {
429         struct cc_usb   *cc;
430         struct termios  termios;
431         int             i;
432
433         if (!tty)
434                 tty = DEFAULT_TTY;
435         cc = calloc (sizeof (struct cc_usb), 1);
436         if (!cc)
437                 return NULL;
438         i = 0;
439         for (;;) {
440                 cc->fd = open(tty, O_RDWR | O_NONBLOCK);
441                 if (cc->fd >= 0)
442                         break;
443                 i++;
444                 if (errno == EBUSY || errno == EPERM || errno == EACCES) {
445                         fprintf(stderr, "open failed, pausing");
446                         perror(tty);
447                         if (i < 20) {
448                                 sleep(3);
449                                 continue;
450                         }
451                 }
452
453                 perror(tty);
454                 free (cc);
455                 return NULL;
456         }
457         tcgetattr(cc->fd, &termios);
458         save_termios = termios;
459         cfmakeraw(&termios);
460         cfsetospeed(&termios, B9600);
461         cfsetispeed(&termios, B9600);
462         tcsetattr(cc->fd, TCSAFLUSH, &termios);
463         cc_usb_printf(cc, "\nE 0\nm 0\n");
464         do {
465                 cc->in_count = cc->in_pos = 0;
466                 _cc_usb_sync(cc, 100, cc_default_timeout);
467         } while (cc->in_count > 0);
468         return cc;
469 }
470
471 void
472 cc_usb_close(struct cc_usb *cc)
473 {
474         cc_usb_close_remote(cc);
475         cc_usb_sync(cc);
476         tcsetattr(cc->fd, TCSAFLUSH, &save_termios);
477         close (cc->fd);
478         free (cc);
479 }
480
481 int
482 cc_usb_write(struct cc_usb *cc, void *buf, int c)
483 {
484         uint8_t *b;
485         int this_time;
486
487         b = buf;
488         cc->show_input = 1;
489         while (c > 0) {
490                 this_time = c;
491                 if (this_time > CC_OUT_BUF - cc->out_count)
492                         this_time = CC_OUT_BUF - cc->out_count;
493                 memcpy(cc->out_buf + cc->out_count, b, this_time);
494                 cc->out_count += this_time;
495                 c -= this_time;
496                 b += this_time;
497                 while (cc->out_count >= CC_OUT_BUF) {
498                         _cc_usb_sync(cc, 0, -1);
499                 }
500         }
501         return 1;
502 }