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