emurom client program
[romdev] / emurom / src / emurom.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <termios.h>
9
10 enum { FMT_BIN, FMT_IHEX, FMT_SREC };
11
12 int send_bin(FILE *infile, int dev);
13 int send_ihex(FILE *infile, int dev);
14 int send_srec(FILE *infile, int dev);
15 void print_usage(const char *argv0);
16
17 FILE *infile;
18 int sdev;
19 int base_addr;
20
21 int main(int argc, char **argv)
22 {
23         int i;
24         char *endp;
25         const char *serdev = "/dev/ttyACM0";
26         const char *fname = 0;
27         struct termios term;
28         int (*send)(FILE*, int) = send_bin;
29
30         for(i=1; i<argc; i++) {
31                 if(argv[i][0] == '-') {
32                         if(argv[i][2] == 0) {
33                                 switch(argv[i][1]) {
34                                 case 'd':
35                                         if(!argv[++i]) {
36                                                 fprintf(stderr, "-d must be followed by a device file\n");
37                                                 return 1;
38                                         }
39                                         serdev = argv[i];
40                                         break;
41
42                                 case 'f':
43                                         if(strcmp(argv[++i], "bin") == 0) {
44                                                 send = send_bin;
45                                         } else if(strcmp(argv[i], "ihex") == 0 || strcmp(argv[i], "hex") == 0) {
46                                                 send = send_ihex;
47                                         } else if(strcmp(argv[i], "srec") == 0) {
48                                                 send = send_srec;
49                                         } else {
50                                                 fprintf(stderr, "invalid input format: %s\n", argv[i]);
51                                                 return 1;
52                                         }
53                                         break;
54
55                                 case 'a':
56                                         base_addr = strtol(argv[++i], &endp, 0);
57                                         if(endp == argv[i]) {
58                                                 fprintf(stderr, "-a must be followed by a base address offset\n");
59                                                 return 1;
60                                         }
61                                         break;
62
63                                 case 'h':
64                                         print_usage(argv[0]);
65                                         return 0;
66
67                                 default:
68                                         fprintf(stderr, "invalid option: %s\n", argv[i]);
69                                         print_usage(argv[0]);
70                                         return 1;
71                                 }
72                         } else {
73                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
74                                 print_usage(argv[0]);
75                                 return 1;
76                         }
77                 } else {
78                         if(fname) {
79                                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
80                                 print_usage(argv[0]);
81                                 return 1;
82                         }
83                         fname = argv[i];
84                 }
85         }
86
87         if(fname) {
88                 if(!(infile = fopen(fname, "rb"))) {
89                         fprintf(stderr, "failed to open input file: %s: %s\n", fname, strerror(errno));
90                         return 1;
91                 }
92         } else {
93                 infile = stdin;
94         }
95
96         if((sdev = open(serdev, O_RDWR | O_NOCTTY)) == -1) {
97                 fprintf(stderr, "failed to connect to devrom board (%s): %s\n", serdev, strerror(errno));
98                 return 1;
99         }
100         tcgetattr(sdev, &term);
101
102         term.c_oflag = 0;
103         term.c_lflag = 0;
104         term.c_cc[VMIN] = 1;
105         term.c_cc[VTIME] = 0;
106
107         term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
108         term.c_iflag = IGNBRK | IGNPAR;
109
110         cfsetispeed(&term, B38400);
111         cfsetospeed(&term, B38400);
112
113         if(tcsetattr(sdev, TCSANOW, &term) < 0) {
114                 fprintf(stderr, "failed to set terminal attributes\n");
115                 return 1;
116         }
117
118         if(send(infile, sdev) == -1) {
119                 return 1;
120         }
121
122         fclose(infile);
123         return 0;
124 }
125
126 int read_line(int fd, char *buf, int bufsz)
127 {
128         int i, num;
129         bufsz--;        /* leave space for the terminator */
130
131         while(bufsz > 0) {
132                 if((num = read(fd, buf, bufsz)) <= 0) {
133                         break;
134                 }
135
136                 for(i=0; i<num; i++) {
137                         if(*buf == '\r' || *buf == '\n') {
138                                 *buf = 0;
139                                 return 0;
140                         }
141                         buf++;
142                 }
143
144                 bufsz -= num;
145                 *buf = 0;
146         }
147
148         return -1;
149 }
150
151 int read_status(int dev)
152 {
153         char buf[128];
154
155         if(read_line(dev, buf, sizeof buf) == -1) {
156                 fprintf(stderr, "failed to read status\n");
157                 return -1;
158         }
159         buf[127] = 0;
160
161         if(memcmp(buf, "ERR", 3) == 0) {
162                 fprintf(stderr, "error:%s\n", buf + 3);
163                 return -1;
164         }
165         return 0;
166 }
167
168 int cmd(int dev, const char *cmd)
169 {
170         write(dev, cmd, strlen(cmd));
171         if(read_status(dev) != 0) {
172                 return -1;
173         }
174         return 0;
175 }
176
177 int cmd_addr(int dev, int addr)
178 {
179         char buf[32];
180         sprintf(buf, "a %d\n", addr);
181         return cmd(dev, buf);
182 }
183
184 int cmd_write(int dev, int val)
185 {
186         char buf[32];
187
188         if(val < 0 || val >= 256) {
189                 fprintf(stderr, "cmd_write: invalid byte value: %d\n", val);
190                 return -1;
191         }
192
193         sprintf(buf, "w %d\n", val);
194         return cmd(dev, buf);
195 }
196
197 int send_bin(FILE *infile, int dev)
198 {
199         int c, res = -1;
200         long count = 0, fsz = -1;
201
202         if(fseek(infile, 0, SEEK_END) != -1) {
203                 fsz = ftell(infile);
204                 rewind(infile);
205         }
206
207         if(cmd(dev, "p\n") == -1 || cmd_addr(dev, 0) == -1) {
208                 return -1;
209         }
210
211         while((c = fgetc(infile)) != -1) {
212                 if(cmd_write(dev, c) == -1) {
213                         goto end;
214                 }
215
216                 if(fsz > 0) {
217                         printf("\r%ld/%ld            ", ++count, fsz);
218                         fflush(stdout);
219                 }
220         }
221         res = 0;
222
223 end:
224         if(fsz > 0) putchar('\n');
225         cmd(dev, "b\n");
226         return res;
227 }
228
229 long hexval(const char *s, int digits)
230 {
231         int i;
232         long val = 0;
233
234         for(i=0; i<digits; i++) {
235                 if(!isxdigit(*s)) {
236                         return -1;
237                 }
238                 val <<= 4;
239
240                 if(*s >= 'a') {
241                         val |= *s - 'a' + 10;
242                 } else if(*s >= 'A') {
243                         val |= *s - 'A' + 10;
244                 } else {
245                         val |= *s - '0';
246                 }
247                 s++;
248         }
249         return val;
250 }
251
252 const char *substr(const char *s, int len)
253 {
254         char *tmp;
255         static char *buf;
256         static int buflen;
257
258         if(len > buflen) {
259                 if(!(tmp = malloc(len + 1))) {
260                         perror("failed to allocate substring buffer");
261                         return 0;
262                 }
263                 free(buf);
264                 buf = tmp;
265                 buflen = len;
266         }
267         memcpy(buf, s, len);
268         buf[len] = 0;
269         return buf;
270 }
271
272 enum {
273         TYPE_DATA               = 0,
274         TYPE_EOF                = 1,
275         TYPE_EXT16              = 2,
276         TYPE_START16    = 3,
277         TYPE_EXT32              = 4,
278         TYPE_START32    = 5
279 };
280
281 int send_ihex(FILE *infile, int dev)
282 {
283         char line[512], *ptr;
284         int res = -1, count, addr, type, val;
285         int cur_addr = -1, offs = 0;
286
287         if(cmd(dev, "p\n") == -1) {
288                 return -1;
289         }
290
291         while(fgets(line, sizeof line, infile)) {
292                 if(line[0] != ':') continue;
293
294                 ptr = line + 1;
295
296                 if((count = hexval(ptr, 2)) == -1) {
297                         fprintf(stderr, "ihex: invalid count field: %s\n", substr(ptr, 2));
298                         goto end;
299                 }
300                 ptr += 2;
301                 if((addr = hexval(ptr, 4)) == -1) {
302                         fprintf(stderr, "ihex: invalid address field: %s\n", substr(ptr, 4));
303                         goto end;
304                 }
305                 ptr += 4;
306                 if((type = hexval(ptr, 2)) == -1 || type > 5) {
307                         fprintf(stderr, "ihex: invalid type field: %s\n", substr(ptr, 2));
308                         goto end;
309                 }
310                 ptr += 2;
311
312                 switch(type) {
313                 case TYPE_DATA:
314                         addr += base_addr + offs;
315                         if(cur_addr != addr) {
316                                 cur_addr = addr;
317                                 if(cmd_addr(dev, addr) == -1) {
318                                         goto end;
319                                 }
320                         }
321
322                         cur_addr += count;
323                         while(count-- > 0) {
324                                 if((val = hexval(ptr, 2)) == -1) {
325                                         fprintf(stderr, "ihex: invalid value: %s\n", substr(ptr, 2));
326                                         goto end;
327                                 }
328                                 if(cmd_write(dev, val) == -1) {
329                                         goto end;
330                                 }
331                                 ptr += 2;
332                         }
333                         break;
334
335                 case TYPE_EOF:
336                         res = 0;
337                         goto end;
338
339                 case TYPE_EXT16:
340                         if((val = hexval(ptr, 4)) == -1) {
341                                 fprintf(stderr, "ihex: invalid extended segment address: %s\n", substr(ptr, 4));
342                                 goto end;
343                         }
344                         offs = val << 4;
345                         ptr += 4;
346                         break;
347
348                 case TYPE_EXT32:
349                         if((val = hexval(ptr, 8)) == -1) {
350                                 fprintf(stderr, "ihex: invalid extended linear address: %s\n", substr(ptr, 8));
351                                 goto end;
352                         }
353                         offs = val;
354                         ptr += 8;
355                         break;
356
357                 default:
358                         break;
359                 }
360
361         }
362         res = 0;
363
364 end:
365         cmd(dev, "b\n");
366         return res;
367 }
368
369 int send_srec(FILE *infile, int dev)
370 {
371         fprintf(stderr, "TODO: SREC format not implemented yet\n");
372         return -1;
373 }
374
375 void print_usage(const char *argv0)
376 {
377         printf("Usage: %s [options] [input file]\n", argv0);
378         printf("If no input file is specified, defaults to reading standard input\n");
379         printf("Options:\n");
380         printf("  -d <device file> devrom board USB serial port (default: /dev/ttyACM0)\n");
381         printf("  -f <input format> format of the input file [bin/ihex/srec] (default: bin)\n");
382         printf("  -a <base address> address offset (default: 0)\n");
383         printf("  -h print usage and exit\n");
384 }