Add document about internal structure
[jelmer/at89prog.git] / prog1.c
1 /* 
2         at89prog
3         (c) 2003-2004 Jelmer Vernooij <jelmer@samba.org>
4
5         This program is free software; you can redistribute it and/or modify
6         it under the terms of the GNU General Public License as published by
7         the Free Software Foundation; either version 2 of the License, or
8         (at your option) any later version.
9
10         This program is distributed in the hope that it will be useful,
11         but WITHOUT ANY WARRANTY; without even the implied warranty of
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13         GNU General Public License for more details.
14
15         You should have received a copy of the GNU General Public License
16         along with this program; if not, write to the Free Software
17         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <stdio.h>
21 #include <popt.h>
22 #include <signal.h>
23 #include <sys/types.h>
24 #include <string.h>
25 #include <sys/io.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include "pins.h"
29 #include "at89ser.h"
30 #include "delays.h"
31
32 #define VERSION         "0.6"
33
34 char progress = 0;
35
36 char *backendname = "serial";
37 char *port = NULL;
38
39 void usage(poptContext pc)
40 {
41         poptPrintHelp(pc, stderr, 0);
42         fprintf(stderr, "\nAvailable commands: \n"
43                         "\terase\n"
44                         "\treset\n"
45                         "\tlock <level>\n"
46                         "\twritefile [file] ...\n"
47                         "\treadfile <len> [file] ...\n"
48                         "\twritebyte <address> <byte>\n"
49                         "\treadbyte <address>\n"
50                         "\tversion\n");
51 }
52
53 void quit(int s)
54 {
55         deactivate();
56         ClosePinBackend();
57         fprintf(stderr, "Received signal, exiting...\n");
58         exit(0);
59 }
60
61 void writechar(char datamem, char do_verify, int address, int byte)
62 {
63         int d;
64         if(datamem)writedata(address, byte);
65         else writecode(address, byte);
66
67         if(do_verify) {
68                 if(datamem)d = readdata(address);
69                 else d = readcode(address);
70
71                 if(byte != d) {
72                         fprintf(stderr, "Error verifying byte at offset 0x%x\n", address);
73                         deactivate();
74                         exit(1);
75                 }
76         }
77 }
78
79 int writebin(FILE *fd, char do_verify, char datamem)
80 {
81         int i = 0;
82         while(!feof(fd)) 
83         {
84                 writechar(datamem, do_verify, i, fgetc(fd));
85                 i++;
86                 if(progress)fputc('.', stderr);
87         }
88         if(progress)fputc('\n', stderr);
89         return 0;
90 }
91
92 int writehex(FILE *fd, char do_verify, char datamem)
93 {
94         int length; int addr1, addr2; int type;
95         long address;
96         int errors = 0;
97         int checksum1, checksum2;
98         int i, j, byte;
99         i = 0;
100         while(!feof(fd)) 
101         {
102                 checksum1 = 0;
103                 i++;
104                 if(fscanf(fd, ":%2x%2x%2x%2x", &length, &addr1, &addr2, &type) < 3) {
105                         fprintf(stderr, "Error reading intel hex file, line %d\n", i);
106                         deactivate();
107                         exit(1);
108                 }
109
110                 checksum1+=length;
111                 checksum1+=type;
112                 checksum1+=addr1;
113                 checksum1+=addr2;
114
115                 address = addr1 * 0x100 + addr2;
116
117                 if(type == 1) break;
118
119                 if(type == 2) continue;
120                 
121                 for(j = 0; j < length; j++) {
122                         if(fscanf(fd, "%2x", &byte) < 1) {
123                                 fprintf(stderr, "Error reading byte %d in intel hex file, line %d\n", j, i);
124                                 deactivate();
125                                 exit(1);
126                         }
127
128                         checksum1+=byte;
129                         writechar(datamem,do_verify,address+j,byte);
130                 }
131
132                 if(fscanf(fd, "%2x", &checksum2) < 1) {
133                         fprintf(stderr, "Error reading checksum in intel hex file, line %d\n", i);
134                         deactivate();
135                         exit(1);
136                 }
137
138                 checksum1 &= 0xFF;
139                 checksum1 = (~checksum1 + 1) & 0xFF;
140
141                 if(checksum1 != checksum2) {
142                         fprintf(stderr, "Warning: checksums do NOT match in intel hex file, line %d\n"
143                         "(%x != %x)\n" 
144                                         "File may be corrupt\n", i, checksum1, checksum2);
145                         errors++;
146                 }
147
148                 while(!feof(fd)) { 
149                         byte = getc(fd); 
150                         if(byte != '\n' && byte != '\r') {
151                                 ungetc(byte, fd);
152                                 break; 
153                         }
154                 }
155
156                 if(progress)fputc('.', stderr);
157         }
158         if(progress)fputc('\n', stderr);
159         return errors;
160 }
161
162
163 int read_pin_configuration(char *name)
164 {
165         char bit[10], pin[10];
166         int line = 0;
167         FILE *fd = fopen(name, "r");
168
169         if(!fd) { 
170                 fprintf(stderr, "Can't open %s!\n"
171                                                 "Type 'man 5 at89prog' for information about the RC file format\n", name);
172                 perror("fopen"); 
173                 return 1; 
174         }
175
176         while(!feof(fd)) 
177         {
178                 line++;
179                 if(fscanf(fd, "%4s %6s\n", bit, pin) < 2) {
180                         fprintf(stderr, "Invalid line %d in %s, ignoring\n", line, name);
181                         continue;
182                 }
183
184                 switch(SetPinVariable(bit, pin)) {
185                 case -1: 
186                         fprintf(stderr, "Invalid configuration variable '%s' at line %d in %s, ignoring\n", bit, line, name);
187                         continue;
188         if(!fd) { 
189                 fprintf(stderr, "Can't open %s!\n"
190                                                 "Type 'man 5 at89prog' for information about the RC file format\n", name);
191                 perror("fopen"); 
192                 return 1; 
193         }
194
195         while(!feof(fd)) 
196         {
197                 line++;
198                 if(fscanf(fd, "%4s %6s\n", bit, pin) < 2) {
199                         fprintf(stderr, "Invalid line %d in %s, ignoring\n", line, name);
200                         continue;
201                 }
202
203                 switch(SetPinVariable(bit, pin)) {
204                 case -1: 
205                         fprintf(stderr, "Invalid configuration variable '%s' at line %d in %s, ignoring\n", bit, line, name);
206                         continue;
207
208                 case -2:
209                         fprintf(stderr, "Invalid value '%s' for configuration variable '%s' at line %d in %s, ignoring\n", pin, bit, line, name);
210                         continue;
211
212                 default:
213                         break;
214                 }
215         }
216         
217         fclose(fd);
218         return 0;
219 }
220
221 int main(int argc, const char **argv) 
222 {
223         int datamem = 0, codemem = 0, verbose = 0, do_verify = 0, ignore_chk = 0;
224         FILE *fd;
225         char *format = "auto";
226         char *rcfile = NULL;
227         char c, print_usage = 1;
228         struct poptOption long_options[] = {
229                 POPT_AUTOHELP
230                 { "data-memory", 'd', POPT_ARG_NONE, &datamem, 0, "Write specified file to data memory" },
231                 { "code-memory", 'c', POPT_ARG_NONE, &codemem, 0, "Write specified file to code memory (default)" },
232                 { "format", 'f', POPT_ARG_STRING, &format, 0, "File format (auto,hex,bin)" },
233                 { "ignore-chk", 'i', POPT_ARG_NONE, &ignore_chk, 0, "Don't wait for CHK to confirm RST" },
234                 { "progress", 'P', POPT_ARG_NONE, &progress, 0, "Print progress dots" },
235                 { "verify", 'V', POPT_ARG_NONE, &do_verify, 0, "Verify written bytes" }, 
236                 { "type", 't', POPT_ARG_STRING, &backendname, 't', "Type of port to use (serial, parallel or serial-raw)" },
237                 { "port", 'p', POPT_ARG_STRING, &location, 'p', "Address of port to use" },
238                 { "rcfile", 'r', POPT_ARG_STRING, &rcfile, 0, "Use rc file from specified location" },
239                 { "verbose", 'v', POPT_ARG_NONE, &verbose, 0, "Be verbose" },
240                 POPT_TABLEEND
241         };
242
243         poptContext pc;
244
245         pc = poptGetContext(NULL, argc, argv, long_options, 0);
246         poptSetOtherOptionHelp(pc, "command [file-to-write]");
247
248         while ((c = poptGetNextOpt(pc)) != -1) { }
249
250         if(!poptPeekArg(pc)) 
251         {
252                 usage(pc);
253                 return 0;
254         }
255
256         if(!rcfile) { 
257                 rcfile = malloc(strlen(getenv("HOME")) + 20);
258                 snprintf(rcfile, strlen(getenv("HOME")) + 20, "%s/.at89progrc", getenv("HOME")); 
259         }
260
261         if(rcfile) {
262                 if(read_pin_configuration(rcfile) != 0) return 1;
263         }
264         
265         if(LoadPinBackend(backendname) != 0) return 1;
266         if(port)SetPinVariable(NULL, port);
267         if(BackendInit() != 0) return 1;
268
269         signal(SIGINT, quit);
270         signal(SIGSEGV, quit);
271
272         if(!activate() && !ignore_chk)
273         {
274                 fprintf(stderr, "RST set, but CHK is low\n");
275                 return 1;
276         }
277         
278         if(!strcmp(poptPeekArg(pc), "reset"))
279         { 
280                 print_usage = 0;
281                 deactivate(); 
282                 if(verbose) fprintf(stderr, "Microcontroller has been reset.\n");
283         } else if(!strcmp(poptPeekArg(pc), "erase"))
284         {
285                 print_usage = 0;
286                 programming();
287                 erase();
288                 if(verbose) fprintf(stderr, "Microcontroller memory has been erased.\n");
289         } else if(!strcmp(poptPeekArg(pc), "lock"))
290         {
291                 int lock_level;
292                 poptGetArg(pc);
293                 lock_level = atoi(poptGetArg(pc));
294                 print_usage = 0;
295                 programming();
296                 lock(lock_level);
297                 
298                 if(verbose) fprintf(stderr, "Locked at level %d\n", lock_level);
299         } else if(!strcmp(poptPeekArg(pc), "writefile"))
300         {
301                 int errors;
302                 poptGetArg(pc);
303                 programming();
304                 while(poptPeekArg(pc)) {
305                         int firstchar;
306                         const char *filename = poptGetArg(pc);
307                         
308                         if(!strcmp(filename, "-")) fd = stdin;
309                         else { 
310                                 fd = fopen(filename, "r");
311                                 if(!fd) {
312                                         fprintf(stderr, "Unable to open file %s, ignoring.\n", filename);
313                                         continue;
314                                 }
315                         }
316
317                         firstchar = getc(fd);
318                         ungetc(firstchar, fd);
319
320                         if(!strcmp(format, "hex") || (!strcmp(format, "auto") && firstchar == ':'))errors = writehex(fd, do_verify, datamem);
321                         else if(!strcmp(format, "auto") || !strcmp(format, "bin"))errors = writebin(fd, do_verify, datamem);
322                         else {
323                                 fprintf(stderr, "Unknown format %s, ignoring file\n", format);
324                                 fclose(fd);
325                                 continue;
326                         }
327
328                         if(!errors && verbose)fprintf(stderr, "File %s programmed correctly\n", filename);
329                         else if(verbose)fprintf(stderr, "File %s programmed with %d errors\n", filename, errors);
330                         fclose(fd);
331                 }
332         } else if(!strcmp(poptPeekArg(pc), "readfile")) {
333                 int len, i;
334                 programming();
335                 poptGetArg(pc);
336
337                 if(!poptPeekArg(pc)) {
338                         fprintf(stderr, "readfile needs at least one argument (number of bytes to read\n");
339                         deactivate();
340                         return 1;
341                 }
342
343                 len = atol(poptGetArg(pc));
344                 
345                 if(!poptPeekArg(pc)) fd = stdout;
346                 else {
347                         fd = fopen(poptGetArg(pc), "w+");
348                         if(!fd) {
349                                 perror("fopen");
350                                 deactivate();
351                                 return 1;
352                         }
353                 }
354
355                 if(!strcmp(format, "bin"))fprintf(stderr, "Warning: writing in binary mode\n");
356
357                 for(i = 0; i < len; i++) {
358                         if(datamem)fputc(readdata(i), fd);
359                         else fputc(readcode(i), fd);
360                         if(progress)fputc('.', stderr);
361                 }
362
363                 if(progress)fputc('\n', stderr);
364                         
365                 if(verbose)fprintf(stderr, "%d bytes read\n", len);
366                 fclose(fd);
367         } else if(!strcmp(poptPeekArg(pc), "writebyte")) {
368                 int address, byte;
369                 poptGetArg(pc);
370                 programming();
371                 if(!poptPeekArg(pc)) {
372                         fprintf(stderr, "writebyte requires 2 arguments\n");
373                         deactivate();
374                         return 1;
375                 }
376                 address = strtol(poptGetArg(pc), NULL, 16);
377
378                 if(!poptPeekArg(pc)) {
379                         fprintf(stderr, "writebyte requires 2 arguments\n");
380                         deactivate();
381                         return 1;
382                 }
383                 byte = strtol(poptGetArg(pc), NULL, 16);
384
385                 writechar(datamem, do_verify, address, byte);
386                 if(verbose)fprintf(stderr, "%x written to %x\n", byte, address);
387         } else if(!strcmp(poptPeekArg(pc), "readbyte")) {
388                 int address;
389                 poptGetArg(pc);
390                 programming();
391                 if(!poptPeekArg(pc)) {
392                         fprintf(stderr, "readbyte requires an argument\n");
393                         deactivate();
394                         return 1;
395                 }
396                 address = strtol(poptGetArg(pc), NULL, 16);