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