Fix checksum checking
[jelmer/at89prog.git] / prog1.c
1 #include <stdio.h>
2 #include <popt.h>
3 #include <signal.h>
4 #include <sys/types.h>
5 #include <string.h>
6 #include <sys/io.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include "pins.h"
10 #include "at89ser.h"
11
12 #define VERSION         "0.5.1"
13
14 char progress = 0;
15
16 void usage(poptContext pc)
17 {
18         poptPrintHelp(pc, stderr, 0);
19         fprintf(stderr, "\nAvailable commands: \n"
20                         "\terase\n"
21                         "\treset\n"
22                         "\tlock <level>\n"
23                         "\twritefile [file] ...\n"
24                         "\treadfile <len> [file] ...\n"
25                         "\twritebyte <address> <byte>\n"
26                         "\treadbyte <address>\n"
27                         "\tversion\n");
28 }
29
30 void quit(int s)
31 {
32         deactivate();
33         fprintf(stderr, "Received signal, exiting...\n");
34         exit(0);
35 }
36
37 void writechar(char datamem, char do_verify, int address, int byte)
38 {
39         int d;
40         if(datamem)writedata(address, byte);
41         else writecode(address, byte);
42
43         if(do_verify) {
44                 if(datamem)d = readdata(address);
45                 else d = readcode(address);
46
47                 if(byte != d) {
48                         fprintf(stderr, "Error verifying byte at offset 0x%x\n", address);
49                         deactivate();
50                         exit(1);
51                 }
52         }
53 }
54
55 int writebin(FILE *fd, char do_verify, char datamem)
56 {
57         int i = 0;
58         while(!feof(fd)) 
59         {
60                 writechar(datamem, do_verify, i, fgetc(fd));
61                 i++;
62                 if(progress)fputc('.', stderr);
63         }
64         if(progress)fputc('\n', stderr);
65         return 0;
66 }
67
68 int writehex(FILE *fd, char do_verify, char datamem)
69 {
70         int length; int addr1, addr2; int type;
71         long address;
72         int errors = 0;
73         int checksum1, checksum2;
74         int i, j, byte;
75         i = 0;
76         while(!feof(fd)) 
77         {
78                 checksum1 = 0;
79                 i++;
80                 if(fscanf(fd, ":%2x%2x%2x%2x", &length, &addr1, &addr2, &type) < 3) {
81                         fprintf(stderr, "Error reading intel hex file, line %d\n", i);
82                         deactivate();
83                         exit(1);
84                 }
85
86                 checksum1+=length;
87                 checksum1+=type;
88                 checksum1+=addr1;
89                 checksum1+=addr2;
90
91                 address = addr1 * 0x100 + addr2;
92
93                 if(type == 1) break;
94
95                 if(type == 2) continue;
96                 
97                 for(j = 0; j < length; j++) {
98                         if(fscanf(fd, "%2x", &byte) < 1) {
99                                 fprintf(stderr, "Error reading byte %d in intel hex file, line %d\n", j, i);
100                                 deactivate();
101                                 exit(1);
102                         }
103
104                         checksum1+=byte;
105                         writechar(datamem,do_verify,address+j,byte);
106                 }
107
108                 if(fscanf(fd, "%2x", &checksum2) < 1) {
109                         fprintf(stderr, "Error reading checksum in intel hex file, line %d\n", i);
110                         deactivate();
111                         exit(1);
112                 }
113
114                 if((0x100 - (checksum1 & 0xFF)) != checksum2) {
115                         fprintf(stderr, "Warning: checksums do NOT match in intel hex file, line %d\n"
116                         "(%x != %x)\n" 
117                                         "File may be corrupt\n", i, 0x100 - (checksum1 & 0xFF), checksum2);
118                         errors++;
119                 }
120
121                 while(!feof(fd)) { 
122                         byte = getc(fd); 
123                         if(byte != '\n' && byte != '\r') {
124                                 ungetc(byte, fd);
125                                 break; 
126                         }
127                 }
128
129                 if(progress)fputc('.', stderr);
130         }
131         if(progress)fputc('\n', stderr);
132         return errors;
133 }
134
135
136 int main(int argc, const char **argv) 
137 {
138         int datamem = 0, codemem = 0, verbose = 0, do_verify = 0, ignore_chk = 0;
139         FILE *fd;
140         int newserport = -1;
141         char *format = "auto";
142         char *rcfile = NULL;
143         char c, print_usage = 1;
144         struct poptOption long_options[] = {
145                 POPT_AUTOHELP
146                 { "data-memory", 'd', POPT_ARG_NONE, &datamem, 0, "Write specified file to data memory" },
147                 { "code-memory", 'c', POPT_ARG_NONE, &codemem, 0, "Write specified file to code memory (default)" },
148                 { "format", 'f', POPT_ARG_STRING, &format, 0, "File format (auto,hex,bin)" },
149                 { "ignore-chk", 'i', POPT_ARG_NONE, &ignore_chk, 0, "Don't wait for CHK to confirm RST" },
150                 { "progress", 'P', POPT_ARG_NONE, &progress, 0, "Print progress dots" },
151                 { "verify", 'V', POPT_ARG_NONE, &do_verify, 0, "Verify written bytes" }, 
152                 { "port", 'p', POPT_ARG_STRING, NULL, 'p', "Address of serial port to use [3f8]" },
153                 { "rcfile", 'r', POPT_ARG_STRING, &rcfile, 0, "Use rc file from specified location" },
154                 { "verbose", 'v', POPT_ARG_NONE, &verbose, 0, "Be verbose" },
155                 POPT_TABLEEND
156         };
157
158         poptContext pc;
159
160         pc = poptGetContext(NULL, argc, argv, long_options, POPT_CONTEXT_KEEP_FIRST);
161         poptSetOtherOptionHelp(pc, "command [file-to-write]");
162
163         while ((c = poptGetNextOpt(pc)) != -1) {
164                 switch(c) {
165                         case 'p': newserport = strtol(poptGetOptArg(pc), NULL, 16); break;
166                                 }
167         }
168
169         if(!rcfile) { 
170                 rcfile = malloc(strlen(getenv("HOME")) + 20);
171                 snprintf(rcfile, strlen(getenv("HOME")) + 20, "%s/.at89progrc", getenv("HOME")); 
172         }
173         
174         if(rcfile) {
175                 if(readrcfile(rcfile) != 0) return 1;
176         }
177
178         if(newserport != -1)serport = newserport;
179
180         if(ioperm(serport, 7, 1) == -1) 
181         {
182                 perror("ioperm");
183                 fprintf(stderr, "Run at89prog with IO port access\n");
184                 return 1;
185         }
186
187         if(ioperm(0x80, 1, 1) == -1) 
188         {
189                 perror("ioperm");
190                 return 1;
191         }
192
193         signal(SIGINT, quit);
194         signal(SIGSEGV, quit);
195
196         poptGetArg(pc); /* drop argv[0] */
197
198         if(!poptPeekArg(pc)) 
199         {
200                 usage(pc);
201                 return 0;
202         }
203
204         if(!activate() && !ignore_chk)
205         {
206                 fprintf(stderr, "RST set, but CHK is low\n");
207                 return 1;
208         }
209         
210         if(!strcmp(poptPeekArg(pc), "reset"))
211         { 
212                 print_usage = 0;
213                 deactivate(); 
214                 if(verbose) fprintf(stderr, "Microcontroller has been reset.\n");
215         } else if(!strcmp(poptPeekArg(pc), "erase"))
216         {
217                 print_usage = 0;
218                 programming();
219                 erase();
220                 if(verbose) fprintf(stderr, "Microcontroller memory has been erased.\n");
221         } else if(!strcmp(poptPeekArg(pc), "lock"))
222         {
223                 int lock_level;
224                 poptGetArg(pc);
225                 lock_level = atoi(poptGetArg(pc));
226                 print_usage = 0;
227                 programming();
228                 lock(lock_level);
229                 
230                 if(verbose) fprintf(stderr, "Locked at level %d\n", lock_level);
231         } else if(!strcmp(poptPeekArg(pc), "writefile"))
232         {
233                 int errors;
234                 poptGetArg(pc);
235                 programming();
236                 while(poptPeekArg(pc)) {
237                         int firstchar;
238                         const char *filename = poptGetArg(pc);
239                         
240                         if(!strcmp(filename, "-")) fd = stdin;
241                         else { 
242                                 fd = fopen(filename, "r");
243                                 if(!fd) {
244                                         fprintf(stderr, "Unable to open file %s, ignoring.\n", filename);
245                                         continue;
246                                 }
247                         }
248
249                         firstchar = getc(fd);
250                         ungetc(firstchar, fd);
251
252                         if(!strcmp(format, "hex") || (!strcmp(format, "auto") && firstchar == ':'))errors = writehex(fd, do_verify, datamem);
253                         else if(!strcmp(format, "auto") || !strcmp(format, "bin"))errors = writebin(fd, do_verify, datamem);
254                         else {
255                                 fprintf(stderr, "Unknown format %s, ignoring file\n", format);
256                                 fclose(fd);
257                                 continue;
258                         }
259
260                         if(!errors && verbose)fprintf(stderr, "File %s programmed correctly\n", filename);
261                         else if(verbose)fprintf(stderr, "File %s programmed with %d errors\n", filename, errors);
262                         fclose(fd);
263                 }
264         } else if(!strcmp(poptPeekArg(pc), "readfile")) {
265                 int len, i;
266                 programming();
267                 poptGetArg(pc);
268
269                 if(!poptPeekArg(pc)) {
270                         fprintf(stderr, "readfile needs at least one argument (number of bytes to read\n");
271                         deactivate();
272                         return 1;
273                 }
274
275                 len = atol(poptGetArg(pc));
276                 
277                 if(!poptPeekArg(pc)) fd = stdout;
278                 else {
279                         fd = fopen(poptGetArg(pc), "w+");
280                         if(!fd) {
281                                 perror("fopen");
282                                 deactivate();
283                                 return 1;
284                         }
285                 }
286
287                 if(!strcmp(format, "bin"))fprintf(stderr, "Warning: writing in binary mode\n");
288
289                 for(i = 0; i < len; i++) {
290                         if(datamem)fputc(readdata(i), fd);
291                         else fputc(readcode(i), fd);
292                         if(progress)fputc('.', stderr);
293                 }
294
295                 if(progress)fputc('\n', stderr);
296                         
297                 if(verbose)fprintf(stderr, "%d bytes read\n", len);
298                 fclose(fd);
299         } else if(!strcmp(poptPeekArg(pc), "writebyte")) {
300                 int address, byte;
301                 poptGetArg(pc);
302                 programming();
303                 if(!poptPeekArg(pc)) {
304                         fprintf(stderr, "writebyte requires 2 arguments\n");
305                         deactivate();
306                         return 1;
307                 }
308                 address = strtol(poptGetArg(pc), NULL, 16);
309
310                 if(!poptPeekArg(pc)) {
311                         fprintf(stderr, "writebyte requires 2 arguments\n");
312                         deactivate();
313                         return 1;
314                 }
315                 byte = strtol(poptGetArg(pc), NULL, 16);
316
317                 writechar(datamem, do_verify, address, byte);
318                 if(verbose)fprintf(stderr, "%x written to %x\n", byte, address);
319         } else if(!strcmp(poptPeekArg(pc), "readbyte")) {
320                 int address;
321                 poptGetArg(pc);
322                 programming();
323                 if(!poptPeekArg(pc)) {
324                         fprintf(stderr, "writebyte requires 2 arguments\n");
325                         deactivate();
326                         return 1;
327                 }
328                 address = strtol(poptGetArg(pc), NULL, 16);
329
330                 if(datamem) printf("%x\n", readdata(address));
331                 else printf("%x\n", readcode(address));
332         } else if(!strcmp(poptPeekArg(pc), "version")) {
333                   fprintf(stderr, "at89prog - a AT89S8252 programmer over the serial port\n");
334                   fprintf(stderr, " Version "VERSION"\n");
335                   fprintf(stderr, " (C) 2003 Jelmer Vernooij <jelmer@samba.org>\n");
336                   fprintf(stderr, "  Published under the GNU GPL\n");
337                   return 0;
338         } else {
339                 fprintf(stderr, "Unknown command %s\n", poptGetArg(pc));
340                 usage(pc);
341         }
342
343         deactivate();
344         poptFreeContext(pc);
345         
346         return 0;
347 }