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