Fix mistake in the verify code, add version string
[bootloader] / cli / boot.c
CommitLineData
9c66c9ff
JM
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
3e6d22f0
JM
14#define VERSION "Swansea Hackspace PIC Bootloader v1"
15
9c66c9ff 16#define BAUD_RATE 19200
e23fe653 17
920f72a8
JM
18#ifdef _WIN32
19#define PORT_NAME "COM4:"
e23fe653 20#else
9c66c9ff 21#define PORT_NAME "/dev/ttyUSB0"
e23fe653
JM
22#endif
23
9c66c9ff
JM
24void usage(const char * name)
25{
3e6d22f0
JM
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");
9c66c9ff
JM
32}
33
34
35#define MIN(a,b) ((a)<(b)?(a):(b))
36
37int 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 */
56int 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];
9c66c9ff
JM
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);
3e6d22f0 125 for (int i=0; i<dev->rowsize; i++) {
9c66c9ff
JM
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
142int 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");
3e6d22f0
JM
180 loge("");
181 usage(argv[0]);
9c66c9ff
JM
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);
e23fe653 202 if (pt == NULL) {
e23fe653
JM
203 return 1;
204 }
9c66c9ff
JM
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}