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