860eb8a5d4778a47f3defdb6d397941bea9c4bed
[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 = "cal", .has_arg = 1, .val = 'c' },
86         { .name = "serial", .has_arg = 1, .val = 's' },
87         { .name = "verbose", .has_arg = 1, .val = 'v' },
88         { 0, 0, 0, 0},
89 };
90
91 static void usage(char *program)
92 {
93         fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] [--cal=<radio-cal>] [--serial=<serial>] file.{elf,ihx}\n", program);
94         exit(1);
95 }
96
97 void
98 done(struct cc_usb *cc, int code)
99 {
100 /*      cc_usb_printf(cc, "a\n"); */
101         cc_usb_close(cc);
102         exit (code);
103 }
104
105 static int
106 ends_with(char *whole, char *suffix)
107 {
108         int whole_len = strlen(whole);
109         int suffix_len = strlen(suffix);
110
111         if (suffix_len > whole_len)
112                 return 0;
113         return strcmp(whole + whole_len - suffix_len, suffix) == 0;
114 }
115
116 int
117 main (int argc, char **argv)
118 {
119         char                    *device = NULL;
120         char                    *filename;
121         Elf                     *e;
122         char                    *serial_end;
123         unsigned int            serial = 0;
124         char                    *serial_ucs2;
125         int                     serial_ucs2_len;
126         char                    serial_int[2];
127         unsigned int            s;
128         int                     i;
129         int                     string_num;
130         uint32_t                cal = 0;
131         char                    cal_int[4];
132         char                    *cal_end;
133         int                     c;
134         int                     was_flashed = 0;
135         struct ao_hex_image     *load;
136         int                     tries;
137         struct cc_usb           *cc = NULL;
138         char                    *tty = NULL;
139         int                     success;
140         int                     verbose = 0;
141         struct ao_sym           *file_symbols;
142         int                     num_file_symbols;
143
144         while ((c = getopt_long(argc, argv, "T:D:c:s:v:", options, NULL)) != -1) {
145                 switch (c) {
146                 case 'T':
147                         tty = optarg;
148                         break;
149                 case 'D':
150                         device = optarg;
151                         break;
152                 case 'c':
153                         cal = strtoul(optarg, &cal_end, 10);
154                         if (cal_end == optarg || *cal_end != '\0')
155                                 usage(argv[0]);
156                         break;
157                 case 's':
158                         serial = strtoul(optarg, &serial_end, 10);
159                         if (serial_end == optarg || *serial_end != '\0')
160                                 usage(argv[0]);
161                         break;
162                 case 'v':
163                         verbose++;
164                         break;
165                 default:
166                         usage(argv[0]);
167                         break;
168                 }
169         }
170
171         ao_verbose = verbose;
172
173         if (verbose > 1)
174                 ccdbg_add_debug(CC_DEBUG_BITBANG);
175
176         filename = argv[optind];
177         if (filename == NULL)
178                 usage(argv[0]);
179
180         if (ends_with (filename, ".elf")) {
181                 load = ao_load_elf(filename, &file_symbols, &num_file_symbols);
182         } else if (ends_with (filename, ".ihx")) {
183                 load = ao_hex_load(filename, &file_symbols, &num_file_symbols);
184         } else
185                 usage(argv[0]);
186
187         if (!ao_editaltos_find_symbols(file_symbols, num_file_symbols, ao_symbols, ao_num_symbols)) {
188                 fprintf(stderr, "Cannot find required symbols\n");
189                 usage(argv[0]);
190         }
191
192         {
193                 int     is_loader;
194                 int     tries;
195
196                 for (tries = 0; tries < 3; tries++) {
197                         char    *this_tty = tty;
198                         if (!this_tty)
199                                 this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
200                         if (!this_tty)
201                                 this_tty = cc_usbdevs_find_by_arg(device, "MegaMetrum");
202                         if (!this_tty)
203                                 this_tty = getenv("ALTOS_TTY");
204                         if (!this_tty)
205                                 this_tty="/dev/ttyACM0";
206
207                         cc = cc_usb_open(this_tty);
208
209                         if (!cc)
210                                 exit(1);
211                         cc_usb_printf(cc, "v\n");
212                         is_loader = 0;
213                         for (;;) {
214                                 char    line[256];
215                                 cc_usb_getline(cc, line, sizeof(line));
216                                 if (!strncmp(line, "altos-loader", 12))
217                                         is_loader = 1;
218                                 if (!strncmp(line, "software-version", 16))
219                                         break;
220                         }
221                         if (is_loader)
222                                 break;
223                         printf ("rebooting to loader\n");
224                         cc_usb_printf(cc, "X\n");
225                         cc_usb_close(cc);
226                         sleep(1);
227                         cc = NULL;
228                 }
229                 if (!is_loader) {
230                         fprintf(stderr, "Cannot switch to boot loader\n");
231                         exit(1);
232                 }
233 #if 0
234                 {
235                         uint8_t check[256];
236                         int     i = 0;
237
238                         ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, check);
239                         for (;;) {
240                                 uint8_t block[256];
241                                 putchar ('.');
242                                 if (++i == 40) {
243                                         putchar('\n');
244                                         i = 0;
245                                 }
246                                 fflush(stdout);
247                                 ao_self_block_write(cc, AO_BOOT_APPLICATION_BASE, block);
248                                 ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, block);
249                                 if (memcmp(block, check, 256) != 0) {
250                                         fprintf (stderr, "read differed\n");
251                                         exit(1);
252                                 }
253                         }
254                 }
255 #endif
256         }
257
258         /* Go fetch existing config values
259          * if available
260          */
261         was_flashed = check_flashed(cc);
262
263         if (!serial) {
264                 if (!was_flashed) {
265                         fprintf (stderr, "Must provide serial number\n");
266                         done(cc, 1);
267                 }
268                 serial = get_uint16(cc, AO_SERIAL_NUMBER);
269                 if (!serial || serial == 0xffff) {
270                         fprintf (stderr, "Invalid existing serial %d\n", serial);
271                         done(cc, 1);
272                 }
273         }
274
275         if (!cal && AO_RADIO_CAL && was_flashed) {
276                 cal = get_uint32(cc, AO_RADIO_CAL);
277                 if (!cal || cal == 0xffffffff) {
278                         fprintf (stderr, "Invalid existing rf cal %d\n", cal);
279                         done(cc, 1);
280                 }
281         }
282
283         if (!ao_editaltos(load, serial, cal))
284                 done(cc, 1);
285
286         /* And flash the resulting image to the device
287          */
288         success = ao_self_write(cc, load);
289                 
290         if (!success) {
291                 fprintf (stderr, "\"%s\": Write failed\n", filename);
292                 done(cc, 1);
293         }
294
295         done(cc, 0);
296 }