ao-tools: Split out USB loader to ao-usbload
[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
190         {
191                 int     is_loader;
192                 int     tries;
193
194                 for (tries = 0; tries < 3; tries++) {
195                         char    *this_tty = tty;
196                         if (!this_tty)
197                                 this_tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
198                         if (!this_tty)
199                                 this_tty = cc_usbdevs_find_by_arg(device, "MegaMetrum");
200                         if (!this_tty)
201                                 this_tty = getenv("ALTOS_TTY");
202                         if (!this_tty)
203                                 this_tty="/dev/ttyACM0";
204
205                         cc = cc_usb_open(this_tty);
206
207                         if (!cc)
208                                 exit(1);
209                         cc_usb_printf(cc, "v\n");
210                         is_loader = 0;
211                         for (;;) {
212                                 char    line[256];
213                                 cc_usb_getline(cc, line, sizeof(line));
214                                 if (!strncmp(line, "altos-loader", 12))
215                                         is_loader = 1;
216                                 if (!strncmp(line, "software-version", 16))
217                                         break;
218                         }
219                         if (is_loader)
220                                 break;
221                         printf ("rebooting to loader\n");
222                         cc_usb_printf(cc, "X\n");
223                         cc_usb_close(cc);
224                         sleep(1);
225                         cc = NULL;
226                 }
227                 if (!is_loader) {
228                         fprintf(stderr, "Cannot switch to boot loader\n");
229                         exit(1);
230                 }
231 #if 0
232                 {
233                         uint8_t check[256];
234                         int     i = 0;
235
236                         ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, check);
237                         for (;;) {
238                                 uint8_t block[256];
239                                 putchar ('.');
240                                 if (++i == 40) {
241                                         putchar('\n');
242                                         i = 0;
243                                 }
244                                 fflush(stdout);
245                                 ao_self_block_write(cc, AO_BOOT_APPLICATION_BASE, block);
246                                 ao_self_block_read(cc, AO_BOOT_APPLICATION_BASE, block);
247                                 if (memcmp(block, check, 256) != 0) {
248                                         fprintf (stderr, "read differed\n");
249                                         exit(1);
250                                 }
251                         }
252                 }
253 #endif
254         }
255
256         /* Go fetch existing config values
257          * if available
258          */
259         was_flashed = check_flashed(cc);
260
261         if (!serial) {
262                 if (!was_flashed) {
263                         fprintf (stderr, "Must provide serial number\n");
264                         done(cc, 1);
265                 }
266                 serial = get_uint16(cc, AO_SERIAL_NUMBER);
267                 if (!serial || serial == 0xffff) {
268                         fprintf (stderr, "Invalid existing serial %d\n", serial);
269                         done(cc, 1);
270                 }
271         }
272
273         if (!cal && AO_RADIO_CAL && was_flashed) {
274                 cal = get_uint32(cc, AO_RADIO_CAL);
275                 if (!cal || cal == 0xffffffff) {
276                         fprintf (stderr, "Invalid existing rf cal %d\n", cal);
277                         done(cc, 1);
278                 }
279         }
280
281         if (!ao_editaltos(load, serial, cal))
282                 done(cc, 1);
283
284         /* And flash the resulting image to the device
285          */
286         success = ao_self_write(cc, load);
287                 
288         if (!success) {
289                 fprintf (stderr, "\"%s\": Write failed\n", filename);
290                 done(cc, 1);
291         }
292
293         done(cc, 0);
294 }