Add manual pages for remaining commands.
[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_READ             16
34 /*
35  * AltOS has different buffer sizes for in/out packets
36  */
37 #define CC_IN_BUF               256
38 #define CC_OUT_BUF              64
39 #define DEFAULT_TTY             "/dev/ttyACM0"
40
41 struct cc_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_count;
50         uint8_t         out_buf[CC_OUT_BUF];
51         int             out_count;
52         struct cc_read  read_buf[CC_NUM_READ];
53         int             read_count;
54 };
55
56 #define NOT_HEX 0xff
57
58 static uint8_t
59 cc_hex_nibble(uint8_t c)
60 {
61         if ('0' <= c && c <= '9')
62                 return c - '0';
63         if ('a' <= c && c <= 'f')
64                 return c - 'a' + 10;
65         if ('A' <= c && c <= 'F')
66                 return c - 'A' + 10;
67         return NOT_HEX;
68 }
69
70 /*
71  * Take raw input bytes, parse them as hex
72  * and write them to the waiting buffer
73  */
74 static void
75 cc_handle_in(struct cc_usb *cc)
76 {
77         uint8_t h, l;
78         int     in_pos;
79         int     read_pos;
80
81         in_pos = 0;
82         read_pos = 0;
83         while (read_pos < cc->read_count && in_pos < cc->in_count) {
84                 /*
85                  * Skip to next hex character
86                  */
87                 while (in_pos < cc->in_count &&
88                        cc_hex_nibble(cc->in_buf[in_pos]) == NOT_HEX)
89                         in_pos++;
90                 /*
91                  * Make sure we have two characters left
92                  */
93                 if (cc->in_count - in_pos < 2)
94                         break;
95                 /*
96                  * Parse hex number
97                  */
98                 h = cc_hex_nibble(cc->in_buf[in_pos]);
99                 l = cc_hex_nibble(cc->in_buf[in_pos+1]);
100                 if (h == NOT_HEX || l == NOT_HEX) {
101                         fprintf(stderr, "hex read error\n");
102                         break;
103                 }
104                 in_pos += 2;
105                 /*
106                  * Store hex number
107                  */
108                 *cc->read_buf[read_pos].buf++ = (h << 4) | l;
109                 if (--cc->read_buf[read_pos].len <= 0)
110                         read_pos++;
111         }
112
113         /* Move remaining bytes to the start of the input buffer */
114         if (in_pos) {
115                 memmove(cc->in_buf, cc->in_buf + in_pos,
116                         cc->in_count - in_pos);
117                 cc->in_count -= in_pos;
118         }
119
120         /* Move pending reads to the start of the array */
121         if (read_pos) {
122                 memmove(cc->read_buf, cc->read_buf + read_pos,
123                         (cc->read_count - read_pos) * sizeof (cc->read_buf[0]));
124                 cc->read_count -= read_pos;
125         }
126
127         /* Once we're done reading, flush any pending input */
128         if (cc->read_count == 0)
129                 cc->in_count = 0;
130 }
131
132 static void
133 cc_usb_dbg(int indent, uint8_t *bytes, int len)
134 {
135         int     eol = 1;
136         int     i;
137         uint8_t c;
138         while (len--) {
139                 c = *bytes++;
140                 if (eol) {
141                         for (i = 0; i < indent; i++)
142                                 ccdbg_debug(CC_DEBUG_BITBANG, " ");
143                         eol = 0;
144                 }
145                 switch (c) {
146                 case '\r':
147                         ccdbg_debug(CC_DEBUG_BITBANG, "^M");
148                         break;
149                 case '\n':
150                         eol = 1;
151                 default:
152                         ccdbg_debug(CC_DEBUG_BITBANG, "%c", c);
153                 }
154         }
155 }
156
157 /*
158  * Flush pending writes, fill pending reads
159  */
160 void
161 cc_usb_sync(struct cc_usb *cc)
162 {
163         int             ret;
164         struct pollfd   fds;
165         int             timeout;
166
167         fds.fd = cc->fd;
168         for (;;) {
169                 if (cc->read_count || cc->out_count)
170                         timeout = -1;
171                 else
172                         timeout = 0;
173                 fds.events = 0;
174                 if (cc->in_count < CC_IN_BUF)
175                         fds.events |= POLLIN;
176                 if (cc->out_count)
177                         fds.events |= POLLOUT;
178                 ret = poll(&fds, 1, timeout);
179                 if (ret == 0)
180                         break;
181                 if (ret < 0) {
182                         perror("poll");
183                         break;
184                 }
185                 if (fds.revents & POLLIN) {
186                         ret = read(cc->fd, cc->in_buf + cc->in_count,
187                                    CC_IN_BUF - cc->in_count);
188                         if (ret > 0) {
189                                 cc_usb_dbg(24, cc->in_buf + cc->in_count, ret);
190                                 cc->in_count += ret;
191                                 cc_handle_in(cc);
192                         } else if (ret < 0)
193                                 perror("read");
194                 }
195                 if (fds.revents & POLLOUT) {
196                         ret = write(cc->fd, cc->out_buf,
197                                     cc->out_count);
198                         if (ret > 0) {
199                                 cc_usb_dbg(0, cc->out_buf, ret);
200                                 memmove(cc->out_buf,
201                                         cc->out_buf + ret,
202                                         cc->out_count - ret);
203                                 cc->out_count -= ret;
204                         } else if (ret < 0)
205                                 perror("write");
206                 }
207         }
208 }
209
210 void
211 cc_usb_printf(struct cc_usb *cc, char *format, ...)
212 {
213         char    buf[1024], *b;
214         va_list ap;
215         int     ret, this_time;
216
217         /* sprintf to a local buffer */
218         va_start(ap, format);
219         ret = vsnprintf(buf, sizeof(buf), format, ap);
220         va_end(ap);
221         if (ret > sizeof(buf)) {
222                 fprintf(stderr, "printf overflow for format %s\n",
223                         format);
224         }
225
226         /* flush local buffer to the wire */
227         b = buf;
228         while (ret > 0) {
229                 this_time = ret;
230                 if (this_time > CC_OUT_BUF - cc->out_count)
231                         this_time = CC_OUT_BUF - cc->out_count;
232                 memcpy(cc->out_buf + cc->out_count, b, this_time);
233                 cc->out_count += this_time;
234                 ret -= this_time;
235                 b += this_time;
236                 while (cc->out_count >= CC_OUT_BUF)
237                         cc_usb_sync(cc);
238         }
239 }
240
241 int
242 cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len)
243 {
244         int     this_len;
245         int     ret = len;
246
247         while (len) {
248                 this_len = len;
249                 if (this_len > 8)
250                         this_len = 8;
251                 len -= this_len;
252                 cc_usb_printf(cc, "P");
253                 while (this_len--)
254                         cc_usb_printf (cc, " %02x", (*bytes++) & 0xff);
255                 cc_usb_printf(cc, "\n");
256         }
257         return ret;
258 }
259
260 void
261 cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len)
262 {
263         struct cc_read  *read_buf;
264         while (cc->read_count >= CC_NUM_READ)
265                 cc_usb_sync(cc);
266         read_buf = &cc->read_buf[cc->read_count++];
267         read_buf->buf = buf;
268         read_buf->len = len;
269 }
270
271 int
272 cc_usb_recv_bytes(struct cc_usb *cc, uint8_t *buf, int len)
273 {
274         cc_queue_read(cc, buf, len);
275         cc_usb_printf(cc, "G %x\n", len);
276         return len;
277 }
278
279 int
280 cc_usb_write_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
281 {
282         cc_usb_printf(cc, "O %x %x\n", len, addr);
283         while (len--)
284                 cc_usb_printf(cc, "%02x", *bytes++);
285         cc_usb_sync(cc);
286         return 0;
287 }
288
289 int
290 cc_usb_read_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
291 {
292         int     i;
293         cc_queue_read(cc, bytes, len);
294         cc_usb_printf(cc, "I %x %x\n", len, addr);
295         cc_usb_sync(cc);
296         for (i = 0; i < len; i++) {
297                 if ((i & 15) == 0) {
298                         if (i)
299                                 ccdbg_debug(CC_DEBUG_MEMORY, "\n");
300                         ccdbg_debug(CC_DEBUG_MEMORY, "\t%04x", addr + i);
301                 }
302                 ccdbg_debug(CC_DEBUG_MEMORY, " %02x", bytes[i]);
303         }
304         ccdbg_debug(CC_DEBUG_MEMORY, "\n");
305         return 0;
306 }
307
308 int
309 cc_usb_debug_mode(struct cc_usb *cc)
310 {
311         cc_usb_sync(cc);
312         cc_usb_printf(cc, "D\n");
313         return 1;
314 }
315
316 int
317 cc_usb_reset(struct cc_usb *cc)
318 {
319         cc_usb_sync(cc);
320         cc_usb_printf(cc, "R\n");
321         return 1;
322 }
323
324 static struct termios   save_termios;
325
326 struct cc_usb *
327 cc_usb_open(char *tty)
328 {
329         struct cc_usb   *cc;
330         struct termios  termios;
331
332         if (!tty)
333                 tty = DEFAULT_TTY;
334         cc = calloc (sizeof (struct cc_usb), 1);
335         if (!cc)
336                 return NULL;
337         cc->fd = open(tty, O_RDWR | O_NONBLOCK);
338         if (cc->fd < 0) {
339                 perror(tty);
340                 free (cc);
341                 return NULL;
342         }
343         tcgetattr(cc->fd, &termios);
344         save_termios = termios;
345         cfmakeraw(&termios);
346         tcsetattr(cc->fd, TCSAFLUSH, &termios);
347         cc_usb_printf(cc, "E 0\nm 0\n");
348         cc_usb_sync(cc);
349         sleep(1);
350         cc_usb_sync(cc);
351         return cc;
352 }
353
354 void
355 cc_usb_close(struct cc_usb *cc)
356 {
357         tcsetattr(cc->fd, TCSAFLUSH, &save_termios);
358         close (cc->fd);
359         free (cc);
360 }