8a522ab0a49ca3063bed8a11c69e068c602358fb
[bootloader] / cli / boot.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <stdint.h>
7
8 #include "log.h"
9 #include "serial.h"
10 #include "protocol.h"
11 #include "devices.h"
12 #include "memory.h"
13
14 #define BAUD_RATE 19200
15
16 #ifdef _WIN32
17 #define PORT_NAME "COM4:"
18 #else 
19 #define PORT_NAME "/dev/ttyUSB0"
20 #endif
21
22 #ifdef _WIN32
23 static void keypress(void)
24 {
25         printf("Press ENTER to exit\n");
26         getchar();
27 }
28 #else
29 static void keypress(void) {}
30 #endif
31
32
33 void usage(const char * name)
34 {
35         loge("Usage: %s [-b baud] [-p port] [-i] [-v] {file.hex}\n", name);
36         loge("-b baud   Set baudrate (default: %d)\n", BAUD_RATE);
37         loge("-p port   Set serial port (default: %s)\n", PORT_NAME);
38         loge("-i        ID Only mode.\n");
39         loge("-v        Verify after write\n");
40 }
41
42
43 #define MIN(a,b) ((a)<(b)?(a):(b))
44
45 int bitmask(int value)
46 {
47         int out = 0;
48         if (value >= (1<<1)) out |= 1;
49         if (value >= (1<<2)) out |= 1<<1;
50         if (value >= (1<<3)) out |= 1<<2;
51         if (value >= (1<<4)) out |= 1<<3;
52         if (value >= (1<<5)) out |= 1<<4;
53         if (value >= (1<<6)) out |= 1<<5;
54         if (value >= (1<<7)) out |= 1<<6;
55         if (value >= (1<<8)) out |= 1<<7;
56         if (value >= (1<<9)) out |= 1<<8;
57         if (value >= (1<<10)) out |= 1<<9;
58
59         return out;
60 }
61 /*
62  * This routine writes a memory plan out to the device
63  */
64 int update_mem(port_t * pt, const devid_t * dev, uint16_t maxmem, mem_t * ram, int verify)
65 {
66         mem_t * block = ram;
67         uint8_t buff[1024];  // scratch space
68
69         for (block=ram; block != NULL; block=block->next) {
70                 int bstart = block->start / 2;
71
72                 /* skip config words, we cant write them anyway */
73                 if (bstart >= 0x8000) {
74                         logd("UpdateMem: skip config block @ %04x", bstart);
75                         continue;
76                 }
77                         
78
79                 uint8_t * p = block->bytes;
80                 int left = block->len;
81                 uint16_t addr = bstart;
82                 int rowlen = dev->rowsize * 2;
83                 logd("UpdateMem: new block %d bytes @ %04x", block->len, bstart);
84
85                 while (left > 0) {
86                         int off = 0;
87                         int len = rowlen;
88
89                         memset(buff, 255, sizeof(buff));
90                         if (addr == bstart) {
91                                 /* first row, align the start */
92                                 if (addr % dev->rowsize != 0) {
93                                         addr &= ~bitmask(dev->rowsize);
94                                         off = (bstart - addr) * 2;
95                                         len -= off;
96                                         logd("UpdateMem: realigning %04X to %04X", bstart, addr);
97                                 }
98                         }
99
100                         len = MIN(len, left);
101
102                         logd("UpdateMem: Preparing %d bytes @ %04X", len, addr);
103
104                         /* partial row write, read first */
105                         if (off != 0 || len < rowlen) {
106                                 logd("UpdateMem: Read %d words @ %04x", dev->rowsize, addr);
107                                 if (loader_readmem(pt, addr, buff, dev->rowsize)) {
108                                         loge("UpdateMem: Aborting on failed read");
109                                         return 1;
110                                 }
111                                 print_memory(addr, buff, dev->rowsize);
112                         }
113
114                         /* update with new values */
115                         memcpy(&buff[off], p, len);
116
117                         /* write the row */
118                         logd("UpdateMem: Writing %d words @ %04x", dev->rowsize, addr);
119                         print_memory(addr, buff, dev->rowsize);
120                         if (loader_writemem(pt, addr, buff, dev->rowsize)) {
121                                 loge("UpdateMem: Aborting on failed write");
122                                 return 1;
123                         }
124
125                         if (verify) {
126                                 uint8_t again[1024];
127                                 logd("UpdateMem: Verify %d words @ %04x", dev->rowsize, addr);
128                                 if (loader_readmem(pt, addr, again, dev->rowsize)) {
129                                         loge("UpdateMem: Aborting on failed read");
130                                         return 1;
131                                 }
132                                 print_memory(addr, again, dev->rowsize);
133                                 for (int i=0; i<rowlen; i++) {
134                                         if (again[i] != buff[i]) {
135                                                 loge("UpdateMem: Verify failed on block 0x%04X", addr);
136                                                 return 1;
137                                         }
138                                 }
139                         }
140
141                         /* shuffle along */
142                         left -= len;
143                         p += len;
144                         addr += dev->rowsize;
145                 }
146         }
147         return 0;
148 }
149
150 int main(int argc, char **argv)
151 {
152         int opt;
153         char * port = NULL;
154         int baud = BAUD_RATE;
155         int verify = 0;
156         int idonly = 0;
157
158         while ((opt=getopt(argc, argv, "h?b:p:ivd"))!=-1) {
159                 switch (opt) {
160                         case 'b':
161                                 baud = atoi(optarg);
162                                 break;
163                         case 'p':
164                                 if (port) free(port);
165                                 port = strdup(optarg);
166                                 break;
167                         case 'v':
168                                 verify=1;
169                                 break;
170                         case 'i':
171                                 idonly=1;
172                                 break;
173                         case 'd':
174                                 debug++;
175                                 break;
176                         case 'h':
177                         case '?':
178                         default:
179                                 usage(argv[0]);
180                                 return 1;
181                 }
182         }
183
184         if (port == NULL) port = PORT_NAME;
185
186         if (!idonly && optind >= argc) {
187                 loge("Error: missing hexfile");
188                 keypress();
189                 return 1;
190         }
191
192         mem_t * ram = NULL;
193
194         if (!idonly) {
195                 FILE * fd = NULL;
196                 if ((fd = fopen(argv[optind], "r"))==NULL) {
197                         loge("Error opening %s: %s", argv[optind], strerror(errno));
198                         keypress();
199                         return 1;
200                 }
201                 ram = load_ihex(fd);
202                 fclose(fd);
203
204                 logd("Memory Summary :-");
205                 list_mem(ram);
206         }
207
208         logd("open serial port %s at %d baud", port, baud);
209         port_t * pt = serial_open(port, baud);
210         if (pt == NULL) {
211                 keypress();
212                 return 1;
213         }
214
215         uint16_t maxmem;
216         uint16_t devid;
217         if (loader_connect(pt, &maxmem, &devid)) {
218                 keypress();
219                 return 1;
220         }
221
222         const devid_t * dev = devid_to_info(devid);
223
224         if (idonly) {
225                 logi("Device ID: %04X", devid);
226                 logi(" Free Mem: %d words available", maxmem);
227                 if (dev != NULL) {
228                         logi(" Dev Name: %s", dev->name );
229                         logi("  Max Mem: %d", dev->memsize );
230                 }
231
232                 keypress();
233                 return 0;
234         }
235         if (dev) logd("Device Name: %s", dev->name);
236
237         /* check that the selected program will fit on this device */
238         if (makesafe_mem(&ram, maxmem)) {
239                 serial_close(pt);
240                 keypress();
241                 return 1;
242         }
243         logd("After re-organisation");
244         list_mem(ram);
245
246         /* now write the updated memory plan to the device */
247         if (!update_mem(pt, dev, maxmem, ram, verify)) {
248                 if (verify)
249                         logi("Device Write (and Verify) Complete");
250                 else
251                         logi("Device Write Complete");
252         }
253
254         /* finished */
255         serial_close(pt);
256         logd("done.");
257         keypress();
258         return 0;
259
260 }