ao-tools/ao-list: Show devices that have no TTY
[fw/altos] / ao-tools / ao-usbload / ao-usbload.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 #include <err.h>
19 #include <fcntl.h>
20 #include <gelf.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <sysexits.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <string.h>
28 #include <stdbool.h>
29 #include "ao-elf.h"
30 #include "ccdbg.h"
31 #include "cc-usb.h"
32 #include "cc.h"
33 #include "ao-usbload.h"
34 #include "ao-selfload.h"
35 #include "ao-verbose.h"
36 #include "ao-editaltos.h"
37
38 static uint16_t
39 get_uint16(struct cc_usb *cc, uint32_t addr)
40 {
41         uint16_t        result;
42         result = ao_self_get_uint16(cc, addr);
43         printf ("read 0x%08x = 0x%04x\n", addr, result);
44         return result;
45 }
46
47 /*
48  * Read a 32-bit value from the target device with arbitrary
49  * alignment
50  */
51 static uint32_t
52 get_uint32(struct cc_usb *cc, uint32_t addr)
53 {
54         uint32_t        result;
55
56         result = ao_self_get_uint32(cc, addr);
57         printf ("read 0x%08x = 0x%08x\n", addr, result);
58         return result;
59 }
60
61 /*
62  * Check to see if the target device has been
63  * flashed with a similar firmware image before
64  *
65  * This is done by looking for the same romconfig version,
66  * which should be at the same location as the linker script
67  * places this at 0x100 from the start of the rom section
68  */
69 static int
70 check_flashed(struct cc_usb *cc)
71 {
72         uint16_t        romconfig_version = get_uint16(cc, AO_ROMCONFIG_VERSION);
73         uint16_t        romconfig_check = get_uint16(cc, AO_ROMCONFIG_CHECK);
74
75         if (romconfig_version != (uint16_t) ~romconfig_check) {
76                 fprintf (stderr, "Device has not been flashed before\n");
77                 return 0;
78         }
79         return 1;
80 }
81
82 static const struct option options[] = {
83         { .name = "tty", .has_arg = 1, .val = 'T' },
84         { .name = "device", .has_arg = 1, .val = 'D' },
85         { .name = "raw", .has_arg = 0, .val = 'r' },
86         { .name = "cal", .has_arg = 1, .val = 'c' },
87         { .name = "serial", .has_arg = 1, .val = 's' },
88         { .name = "verbose", .has_arg = 1, .val = 'v' },
89         { .name = "wait", .has_arg = 0, .val = 'w' },
90         { 0, 0, 0, 0},
91 };
92
93 static void usage(char *program)
94 {
95         fprintf(stderr, "usage: %s [--raw] [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] [--cal=<radio-cal>] [--serial=<serial>] [--wait] file.{elf,ihx}\n", program);
96         exit(1);
97 }
98
99 void
100 done(struct cc_usb *cc, int code)
101 {
102 /*      cc_usb_printf(cc, "a\n"); */
103         cc_usb_close(cc);
104         exit (code);
105 }
106
107 static int
108 ends_with(char *whole, char *suffix)
109 {
110         int whole_len = strlen(whole);
111         int suffix_len = strlen(suffix);
112
113         if (suffix_len > whole_len)
114                 return 0;
115         return strcmp(whole + whole_len - suffix_len, suffix) == 0;
116 }
117
118 int
119 main (int argc, char **argv)
120 {
121         char                    *device = NULL;
122         char                    *filename;
123         Elf                     *e;
124         int                     raw = 0;
125         char                    *serial_end;
126         unsigned int            serial = 0;
127         char                    *serial_ucs2;
128         int                     serial_ucs2_len;
129         char                    serial_int[2];
130         unsigned int            s;
131         int                     i;
132         int                     string_num;
133         uint32_t                cal = 0;
134         char                    cal_int[4];
135         char                    *cal_end;
136         int                     c;
137         int                     was_flashed = 0;
138         struct ao_hex_image     *load;
139         int                     tries;
140         struct cc_usb           *cc = NULL;
141         char                    *tty = NULL;
142         int                     success;
143         int                     verbose = 0;
144         struct ao_sym           *file_symbols;
145         int                     num_file_symbols;
146         uint32_t                flash_base, flash_bound;
147         int                     has_flash_size = 0;
148
149         while ((c = getopt_long(argc, argv, "wrT:D:c:s:v:", options, NULL)) != -1) {
150                 switch (c) {
151                 case 'T':
152                         tty = optarg;
153                         break;
154                 case 'D':
155                         device = optarg;
156                         break;
157                 case 'r':
158                         raw = 1;
159                         break;
160                 case 'w':
161                         cc_default_timeout = -1;
162                         break;
163                 case 'c':
164                         cal = strtoul(optarg, &cal_end, 10);
165                         if (cal_end == optarg || *cal_end != '\0')
166                                 usage(argv[0]);
167                         break;
168                 case 's':
169                         serial = strtoul(optarg, &serial_end, 10);
170                         if (serial_end == optarg || *serial_end != '\0')
171                                 usage(argv[0]);
172                         break;
173                 case 'v':
174                         verbose++;
175                         break;
176                 default:
177                         usage(argv[0]);
178                         break;
179                 }
180         }
181
182         ao_verbose = verbose;
183
184         if (verbose > 1)
185                 ccdbg_add_debug(CC_DEBUG_BITBANG);
186
187         filename = argv[optind];
188         if (filename == NULL)
189                 usage(argv[0]);
190
191         if (ends_with (filename, ".elf")) {
192                 load = ao_load_elf(filename, &file_symbols, &num_file_symbols);
193         } else if (ends_with (filename, ".ihx")) {
194                 load = ao_hex_load(filename, &file_symbols, &num_file_symbols);
195         } else
196                 usage(argv[0]);
197
198         if (!raw) {
199                 if (!ao_editaltos_find_symbols(file_symbols, num_file_symbols, ao_symbols, ao_num_symbols)) {
200                         fprintf(stderr, "Cannot find required symbols\n");
201                         usage(argv[0]);
202                 }
203         }
204
205         {
206                 int     is_loader;
207                 int     tries;
208
209                 for (tries = 0; tries < 3; tries++) {
210                         char    *this_tty = tty;
211                         if (!this_tty)
212                                 this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
213                         if (!this_tty)
214                                 this_tty = cc_usbdevs_find_by_arg(device, "TeleMega");
215                         if (!this_tty)
216                                 this_tty = cc_usbdevs_find_by_arg(device, "TeleMetrum");
217                         if (!this_tty)
218                                 this_tty = cc_usbdevs_find_by_arg(device, "TeleGPS");
219                         if (!this_tty)
220                                 this_tty = getenv("ALTOS_TTY");
221                         if (!this_tty)
222                                 this_tty="/dev/ttyACM0";
223
224                         cc = cc_usb_open(this_tty);
225
226                         if (!cc)
227                                 exit(1);
228                         cc_usb_printf(cc, "v\n");
229                         is_loader = 0;
230                         for (;;) {
231                                 char    line[256];
232                                 cc_usb_getline(cc, line, sizeof(line));
233                                 if (!strncmp(line, "altos-loader", 12))
234                                         is_loader = 1;
235                                 if (!strncmp(line, "flash-range", 11)) {
236                                         int i;
237                                         for (i = 11; i < strlen(line); i++)
238                                                 if (line[i] != ' ')
239                                                         break;
240                                         if (sscanf(line + i, "%x %x", &flash_base, &flash_bound) == 2)
241                                                 has_flash_size = 1;
242                                 }
243                                 if (!strncmp(line, "software-version", 16))
244                                         break;
245                         }
246                         if (is_loader)
247                                 break;
248                         printf ("rebooting to loader\n");
249                         cc_usb_printf(cc, "X\n");
250                         cc_usb_close(cc);
251                         sleep(1);
252                         cc = NULL;
253                 }
254                 if (!is_loader) {
255                         fprintf(stderr, "Cannot switch to boot loader\n");
256                         exit(1);
257                 }
258 #if 0
259                 {
260                         uint8_t check[256];
261                         int     i = 0;
262
263                         ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, check);
264                         for (;;) {
265                                 uint8_t block[256];
266                                 putchar ('.');
267                                 if (++i == 40) {
268                                         putchar('\n');
269                                         i = 0;
270                                 }
271                                 fflush(stdout);
272                                 ao_self_block_write(cc, AO_BOOT_APPLICATION_BASE, block);
273                                 ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, block);
274                                 if (memcmp(block, check, 256) != 0) {
275                                         fprintf (stderr, "read differed\n");
276                                         exit(1);
277                                 }
278                         }
279                 }
280 #endif
281         }
282
283         /* If the device can tell us the size of flash, make sure
284          * the image fits in that
285          */
286         if (has_flash_size) {
287                 if (load->address < flash_base ||
288                     load->address + load->length > flash_bound)
289                 {
290                         fprintf(stderr, "Image does not fit on device.\n");
291                         fprintf(stderr, "  Image base:  %08x bounds %08x\n",
292                                 load->address, load->address + load->length);
293                         fprintf(stderr, "  Device base: %08x bounds %08x\n",
294                                 flash_base, flash_bound);
295                         done(cc, 1);
296                 }
297         }
298
299         if (!raw) {
300                 /* Go fetch existing config values
301                  * if available
302                  */
303                 was_flashed = check_flashed(cc);
304
305                 if (!serial) {
306                         if (!was_flashed) {
307                                 fprintf (stderr, "Must provide serial number\n");
308                                 done(cc, 1);
309                         }
310                         serial = get_uint16(cc, AO_SERIAL_NUMBER);
311                         if (!serial || serial == 0xffff) {
312                                 fprintf (stderr, "Invalid existing serial %d\n", serial);
313                                 done(cc, 1);
314                         }
315                 }
316
317                 if (!cal && AO_RADIO_CAL && was_flashed) {
318                         cal = get_uint32(cc, AO_RADIO_CAL);
319                         if (!cal || cal == 0xffffffff) {
320                                 fprintf (stderr, "Invalid existing rf cal %d\n", cal);
321                                 done(cc, 1);
322                         }
323                 }
324
325                 if (!ao_editaltos(load, serial, cal))
326                         done(cc, 1);
327         }
328
329         /* And flash the resulting image to the device
330          */
331         success = ao_self_write(cc, load);
332
333         if (!success) {
334                 fprintf (stderr, "\"%s\": Write failed\n", filename);
335                 done(cc, 1);
336         }
337
338         done(cc, 0);
339 }