altosui/altosuilib/altoslib: Move more stuff out of autosui. Reduce site map memory
[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
57         int                     remote;
58 };
59
60 #define NOT_HEX 0xff
61
62 static uint8_t
63 cc_hex_nibble(uint8_t c)
64 {
65         if ('0' <= c && c <= '9')
66                 return c - '0';
67         if ('a' <= c && c <= 'f')
68                 return c - 'a' + 10;
69         if ('A' <= c && c <= 'F')
70                 return c - 'A' + 10;
71         return NOT_HEX;
72 }
73
74 /*
75  * Take raw input bytes, parse them as hex
76  * and write them to the waiting buffer
77  */
78 static void
79 cc_handle_hex_read(struct cc_usb *cc)
80 {
81         uint8_t h, l;
82         int     hex_pos;
83
84         hex_pos = 0;
85         while (hex_pos < cc->hex_count && cc->in_pos < cc->in_count) {
86                 /*
87                  * Skip to next hex character
88                  */
89                 while (cc->in_pos < cc->in_count &&
90                        cc_hex_nibble(cc->in_buf[cc->in_pos]) == NOT_HEX)
91                         cc->in_pos++;
92                 /*
93                  * Make sure we have two characters left
94                  */
95                 if (cc->in_count - cc->in_pos < 2)
96                         break;
97                 /*
98                  * Parse hex number
99                  */
100                 h = cc_hex_nibble(cc->in_buf[cc->in_pos]);
101                 l = cc_hex_nibble(cc->in_buf[cc->in_pos+1]);
102                 if (h == NOT_HEX || l == NOT_HEX) {
103                         fprintf(stderr, "hex read error\n");
104                         break;
105                 }
106                 cc->in_pos += 2;
107                 /*
108                  * Store hex number
109                  */
110                 *cc->hex_buf[hex_pos].buf++ = (h << 4) | l;
111                 if (--cc->hex_buf[hex_pos].len <= 0)
112                         hex_pos++;
113         }
114
115         /* Move pending hex reads to the start of the array */
116         if (hex_pos) {
117                 memmove(cc->hex_buf, cc->hex_buf + hex_pos,
118                         (cc->hex_count - hex_pos) * sizeof (cc->hex_buf[0]));
119                 cc->hex_count -= hex_pos;
120         }
121 }
122
123 static void
124 cc_usb_dbg(int indent, uint8_t *bytes, int len)
125 {
126         static int      eol = 1;
127         int     i;
128         uint8_t c;
129         ccdbg_debug(CC_DEBUG_BITBANG, "<<<%d bytes>>>", len);
130         while (len--) {
131                 c = *bytes++;
132                 if (eol) {
133                         for (i = 0; i < indent; i++)
134                                 ccdbg_debug(CC_DEBUG_BITBANG, " ");
135                         eol = 0;
136                 }
137                 switch (c) {
138                 case '\r':
139                         ccdbg_debug(CC_DEBUG_BITBANG, "\\r");
140                         break;
141                 case '\n':
142                         eol = 1;
143                         ccdbg_debug(CC_DEBUG_BITBANG, "\\n\n");
144                         break;
145                 default:
146                         if (c < ' ' || c > '~')
147                                 ccdbg_debug(CC_DEBUG_BITBANG, "\\%02x", c);
148                         else
149                                 ccdbg_debug(CC_DEBUG_BITBANG, "%c", c);
150                 }
151         }
152 }
153
154 /*
155  * Flush pending writes, fill pending reads
156  */
157
158 static int
159 _cc_usb_sync(struct cc_usb *cc, int wait_for_input)
160 {
161         int             ret;
162         struct pollfd   fds;
163         int             timeout;
164
165         fds.fd = cc->fd;
166         for (;;) {
167                 if (cc->hex_count || cc->out_count)
168                         timeout = 5000;
169                 else if (wait_for_input && cc->in_pos == cc->in_count)
170                         timeout = wait_for_input;
171                 else
172                         timeout = 0;
173                 fds.events = 0;
174                 /* Move remaining bytes to the start of the input buffer */
175                 if (cc->in_pos) {
176                         memmove(cc->in_buf, cc->in_buf + cc->in_pos,
177                                 cc->in_count - cc->in_pos);
178                         cc->in_count -= cc->in_pos;
179                         cc->in_pos = 0;
180                 }
181                 if (cc->in_count < CC_IN_BUF)
182                         fds.events |= POLLIN;
183                 if (cc->out_count)
184                         fds.events |= POLLOUT;
185                 ret = poll(&fds, 1, timeout);
186                 if (ret == 0) {
187                         if (timeout)
188                                 return -1;
189                         break;
190                 }
191                 if (ret < 0) {
192                         perror("poll");
193                         return -1;
194                 }
195                 if (fds.revents & POLLIN) {
196                         ret = read(cc->fd, cc->in_buf + cc->in_count,
197                                    CC_IN_BUF - cc->in_count);
198                         if (ret > 0) {
199                                 cc_usb_dbg(24, cc->in_buf + cc->in_count, ret);
200                                 cc->in_count += ret;
201                                 if (cc->hex_count)
202                                         cc_handle_hex_read(cc);
203                         } else if (ret < 0)
204                                 perror("read");
205                 }
206                 if (fds.revents & POLLOUT) {
207                         ret = write(cc->fd, cc->out_buf,
208                                     cc->out_count);
209                         if (ret > 0) {
210                                 cc_usb_dbg(0, cc->out_buf, ret);
211                                 memmove(cc->out_buf,
212                                         cc->out_buf + ret,
213                                         cc->out_count - ret);
214                                 cc->out_count -= ret;
215                         } else if (ret < 0)
216                                 perror("write");
217                 }
218         }
219         return 0;
220 }
221
222 void
223 cc_usb_sync(struct cc_usb *cc)
224 {
225         if (_cc_usb_sync(cc, 0) < 0) {
226                 fprintf(stderr, "USB link timeout\n");
227                 exit(1);
228         }
229 }
230
231 void
232 cc_usb_printf(struct cc_usb *cc, char *format, ...)
233 {
234         char    buf[1024], *b;
235         va_list ap;
236         int     ret, this_time;
237
238         /* sprintf to a local buffer */
239         va_start(ap, format);
240         ret = vsnprintf(buf, sizeof(buf), format, ap);
241         va_end(ap);
242         if (ret > sizeof(buf)) {
243                 fprintf(stderr, "printf overflow for format %s\n",
244                         format);
245         }
246
247         /* flush local buffer to the wire */
248         b = buf;
249         while (ret > 0) {
250                 this_time = ret;
251                 if (this_time > CC_OUT_BUF - cc->out_count)
252                         this_time = CC_OUT_BUF - cc->out_count;
253                 memcpy(cc->out_buf + cc->out_count, b, this_time);
254                 cc->out_count += this_time;
255                 ret -= this_time;
256                 b += this_time;
257                 while (cc->out_count >= CC_OUT_BUF)
258                         cc_usb_sync(cc);
259         }
260 }
261
262 int
263 cc_usb_getchar_timeout(struct cc_usb *cc, int timeout)
264 {
265         while (cc->in_pos == cc->in_count) {
266                 if (_cc_usb_sync(cc, timeout) < 0) {
267                         fprintf(stderr, "USB link timeout\n");
268                         exit(1);
269                 }
270         }
271         return cc->in_buf[cc->in_pos++];
272 }
273
274 int
275 cc_usb_getchar(struct cc_usb *cc)
276 {
277         return cc_usb_getchar_timeout(cc, 5000);
278 }
279
280 void
281 cc_usb_getline(struct cc_usb *cc, char *line, int max)
282 {
283         int     c;
284
285         while ((c = cc_usb_getchar(cc)) != '\n') {
286                 switch (c) {
287                 case '\r':
288                         break;
289                 default:
290                         if (max > 1) {
291                                 *line++ = c;
292                                 max--;
293                         }
294                         break;
295                 }
296         }
297         *line++ = '\0';
298 }
299
300 int
301 cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len)
302 {
303         int     this_len;
304         int     ret = len;
305
306         while (len) {
307                 this_len = len;
308                 if (this_len > 8)
309                         this_len = 8;
310                 len -= this_len;
311                 cc_usb_printf(cc, "P");
312                 while (this_len--)
313                         cc_usb_printf (cc, " %02x", (*bytes++) & 0xff);
314                 cc_usb_printf(cc, "\n");
315         }
316         return ret;
317 }
318
319 void
320 cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len)
321 {
322         struct cc_hex_read      *hex_buf;
323
324         /* At the start of a command sequence, flush any pending input */
325         if (cc->hex_count == 0) {
326                 cc_usb_sync(cc);
327                 cc->in_count = 0;
328         }
329         while (cc->hex_count >= CC_NUM_HEX_READ)
330                 cc_usb_sync(cc);
331         hex_buf = &cc->hex_buf[cc->hex_count++];
332         hex_buf->buf = buf;
333         hex_buf->len = len;
334 }
335
336 int
337 cc_usb_recv_bytes(struct cc_usb *cc, uint8_t *buf, int len)
338 {
339         cc_queue_read(cc, buf, len);
340         cc_usb_printf(cc, "G %x\n", len);
341         return len;
342 }
343
344 int
345 cc_usb_write_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
346 {
347         cc_usb_printf(cc, "O %x %x\n", len, addr);
348         while (len--)
349                 cc_usb_printf(cc, "%02x", *bytes++);
350         cc_usb_sync(cc);
351         return 0;
352 }
353
354 int
355 cc_usb_read_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
356 {
357         int     i;
358         cc_queue_read(cc, bytes, len);
359         cc_usb_printf(cc, "I %x %x\n", len, addr);
360         cc_usb_sync(cc);
361         for (i = 0; i < len; i++) {
362                 if ((i & 15) == 0) {
363                         if (i)
364                                 ccdbg_debug(CC_DEBUG_MEMORY, "\n");
365                         ccdbg_debug(CC_DEBUG_MEMORY, "\t%04x", addr + i);
366                 }
367                 ccdbg_debug(CC_DEBUG_MEMORY, " %02x", bytes[i]);
368         }
369         ccdbg_debug(CC_DEBUG_MEMORY, "\n");
370         return 0;
371 }
372
373 int
374 cc_usb_debug_mode(struct cc_usb *cc)
375 {
376         cc_usb_sync(cc);
377         cc_usb_printf(cc, "D\n");
378         return 1;
379 }
380
381 int
382 cc_usb_reset(struct cc_usb *cc)
383 {
384         cc_usb_sync(cc);
385         cc_usb_printf(cc, "R\n");
386         return 1;
387 }
388
389 void
390 cc_usb_open_remote(struct cc_usb *cc, int freq, char *call)
391 {
392         if (!cc->remote) {
393                 fprintf (stderr, "freq %dkHz\n", freq);
394                 fprintf (stderr, "call %s\n", call);
395                 cc_usb_printf(cc, "\nc F %d\nc c %s\np\nE 0\n", freq, call);
396                 do {
397                         cc->in_count = cc->in_pos = 0;
398                         _cc_usb_sync(cc, 100);
399                 } while (cc->in_count > 0);
400                 cc->remote = 1;
401         }
402 }
403
404 void
405 cc_usb_close_remote(struct cc_usb *cc)
406 {
407         if (cc->remote) {
408                 cc_usb_printf(cc, "~");
409                 cc->remote = 0;
410         }
411 }
412
413 static struct termios   save_termios;
414
415 #include <errno.h>
416
417 struct cc_usb *
418 cc_usb_open(char *tty)
419 {
420         struct cc_usb   *cc;
421         struct termios  termios;
422         int             i;
423
424         if (!tty)
425                 tty = DEFAULT_TTY;
426         cc = calloc (sizeof (struct cc_usb), 1);
427         if (!cc)
428                 return NULL;
429         i = 0;
430         for (;;) {
431                 cc->fd = open(tty, O_RDWR | O_NONBLOCK);
432                 if (cc->fd >= 0)
433                         break;
434                 i++;
435                 if (errno == EBUSY || errno == EPERM || errno == EACCES) {
436                         fprintf(stderr, "open failed, pausing");
437                         perror(tty);
438                         if (i < 20) {
439                                 sleep(3);
440                                 continue;
441                         }
442                 }
443
444                 perror(tty);
445                 free (cc);
446                 return NULL;
447         }
448         tcgetattr(cc->fd, &termios);
449         save_termios = termios;
450         cfmakeraw(&termios);
451         cfsetospeed(&termios, B9600);
452         cfsetispeed(&termios, B9600);
453         tcsetattr(cc->fd, TCSAFLUSH, &termios);
454         cc_usb_printf(cc, "\nE 0\nm 0\n");
455         do {
456                 cc->in_count = cc->in_pos = 0;
457                 _cc_usb_sync(cc, 100);
458         } while (cc->in_count > 0);
459         return cc;
460 }
461
462 void
463 cc_usb_close(struct cc_usb *cc)
464 {
465         cc_usb_close_remote(cc);
466         cc_usb_sync(cc);
467         tcsetattr(cc->fd, TCSAFLUSH, &save_termios);
468         close (cc->fd);
469         free (cc);
470 }