fixed 04 record parsing
[my-code/arm.git] / betty / lpcload.c
1 /*
2  * lpcload.c - load firmware into ram of lpc2220 via uart0
3  *
4  * author: hackbard@hackdaworld.org, rolf.anders@physik.uni-augsburg.de
5  *
6  * build: make
7  * usage: sudo ./lpcload -d /dev/ttyS0 -f firmware.hex [-v]
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <termios.h>
18
19 #define VERBOSE                 (1<<0)
20 #define FIRMWARE                (1<<1)
21
22 #define TXRX_TYPE_SYNC          0x00
23 #define TXRX_TYPE_CKSM          0x00
24 #define TXRX_TYPE_BAUD          0x01
25 #define TXRX_TYPE_CMD           0x02
26 #define TXRX_TYPE_DATA          0x03
27 #define TXRX_TYPE_GO            0x04
28
29 #define CMD_SUCCESS             "0\r\n"
30 #define INVALID_COMMAND         "1\r\n"
31 #define SRC_ADDR_ERROR          "2\r\n"
32 #define DST_ADDR_ERROR          "3\r\n"
33 #define SRC_ADDR_NOT_MAPPED     "4\r\n"
34 #define DST_ADDR_NOT_MAPPED     "5\r\n"
35 #define COUNT_ERROR             "6\r\n"
36 #define COMPARE_ERROR           "10\r\n"
37 #define BUSY                    "11\r\n"
38 #define PARAM_ERROR             "12\r\n"
39 #define ADDR_ERROR              "13\r\n"
40 #define ADDR_NOT_MAPPED         "14\r\n"
41 #define CMD_LOCKED              "15\r\n"
42 #define INVALID_CODE            "16\r\n"
43 #define INVALID_BAUD_RATE       "17\r\n"
44 #define INVALID_STOP_BIT        "18\r\n"
45
46 #define CRYSTFREQ               "10000"
47 #define RAMOFFSET               0x40000200
48
49 #define BUFSIZE                 128
50
51 typedef unsigned char u8;
52 typedef unsigned short u16;
53 typedef unsigned int u32;
54
55 typedef struct s_lpc {
56         int sfd;                /* serial fd */
57         char sdev[128];         /* seriel device */
58         int fwfd;               /* fimrware fd */
59         char fwfile[128];       /* firmware file */
60         u8 info;                /* info/mode */
61         char freq[8];           /* frequency */
62         u32 roff;               /* ram offset of uc */
63 } t_lpc;
64
65 void usage(void) {
66
67         printf("possible argv:\n");
68         printf("  -d <serial device>\n");
69         printf("  -f <firmware>\n");
70         printf("  -c <crystal freq>\n");
71         printf("  -r <ram offset>\n");
72         printf("  -v\n");
73
74 }
75
76 int open_serial_device(t_lpc *lpc) {
77
78         struct termios term;
79
80         //memset(&term,0,sizeof(struct termios));
81
82         /* open serial device */
83
84         lpc->sfd=open(lpc->sdev,O_RDWR);
85         if(lpc->sfd<0) {
86                 perror("tts open");
87                 return lpc->sfd;
88         }
89
90         /* configure the serial device */
91
92         tcgetattr(lpc->sfd,&term);
93
94         // input/output baudrate
95
96         cfsetispeed(&term,B38400);
97         cfsetospeed(&term,B38400);
98
99         // control options -> 8n1
100
101         term.c_cflag&=~PARENB;  // no parity
102         term.c_cflag&=~CSTOPB;  // only 1 stop bit
103         term.c_cflag&=~CSIZE;   // no bit mask for data bits
104         term.c_cflag|=CS8;      // 8 data bits
105
106         // line options -> raw input
107         
108         term.c_lflag&=~(ICANON|ECHO|ECHOE|ISIG);
109
110         // input options -> enable flow control
111         
112         //term.c_iflag&=~(IXON|IXOFF|IXANY|INLCR|ICRNL);
113         term.c_iflag&=~(INLCR|ICRNL|IXANY);
114         term.c_iflag|=(IXON|IXOFF);
115         
116         // output options
117
118         term.c_oflag=0;
119
120         // more control options -> timeout / flow control
121         
122         term.c_cc[VMIN]=0;
123         term.c_cc[VTIME]=10;    // 1 second timeout
124         term.c_cc[VSTART]=0x11;
125         term.c_cc[VSTOP]=0x13;
126
127         tcsetattr(lpc->sfd,TCSANOW,&term);
128
129         return lpc->sfd;
130 }
131
132 int open_firmware(t_lpc *lpc) {
133
134         /* open firmware file */
135
136         lpc->fwfd=open(lpc->fwfile,O_RDONLY);
137
138         if(lpc->fwfd<0)
139                 perror("fw open");
140
141         return lpc->fwfd;
142 }
143
144 int txrx(t_lpc *lpc,char *buf,int len,u8 type) {
145
146         int ret,cnt;
147         int i;
148
149         /* write */
150
151         if(lpc->info&VERBOSE)
152                 printf("  >> ");
153         cnt=0;
154         while(len) {
155                 ret=write(lpc->sfd,buf+cnt,len);
156                 if(ret<0) {
157                         perror("txrx write");
158                         return ret;
159                 }
160                 if(lpc->info&VERBOSE)
161                         for(i=0;i<ret;i++)
162                                 printf("%c",
163                                        ((buf[cnt+i]>0x19)&(buf[cnt+i]<0x7f))?
164                                        buf[cnt+i]:'.');
165                 len-=ret;
166                 cnt+=ret;
167         }
168         if(lpc->info&VERBOSE) {
169                 printf(" | ");
170                 for(i=0;i<cnt;i++)
171                         printf("%02x ",buf[i]);
172                 printf("| (%d)\n",cnt);
173         }
174
175         /* cut the echo if not of type auto baud */
176
177         if(type!=TXRX_TYPE_BAUD) {
178                 while(cnt) {
179                         ret=read(lpc->sfd,buf,cnt);
180                         if(ret<0) {
181                                 perror("txrx echo cut");
182                                 return ret;
183                         }
184                         cnt-=ret;
185                 }
186         }
187
188         /* return here if type is data */
189
190         if(type==TXRX_TYPE_DATA)
191                 return cnt;
192
193         /* read */
194
195         ret=read(lpc->sfd,buf,1);
196         if(ret<0) {
197                 perror("txrx read (first byte)");
198                 return ret;
199         }
200                 
201         switch(buf[0]) {
202                 case 'S':
203                         cnt=13;
204                         break;
205                 case 'O':
206                         cnt=3;
207                         break;
208                 case 'R':
209                         cnt=7;
210                         break;
211                 case '0':
212                         cnt=2;
213                         break;
214                 default:
215                         printf("txrx read: bad return byte '%02x'\n",buf[0]);
216                         break;
217         }
218
219         ret=1;
220         i=cnt;
221         while(i) {
222                 ret=read(lpc->sfd,buf+1+cnt-i,i);
223                 if(ret<0) {
224                         perror("txrx read (next bytes)");
225                         return ret;
226                 }
227                 i-=ret;
228         }
229         if(lpc->info&VERBOSE) {
230                 printf("  << ");
231                 for(i=0;i<cnt+1;i++)
232                         printf("%c",((buf[i]>0x19)&(buf[i]<0x7f))?
233                                     buf[i]:'.');
234                 printf(" | ");
235                 for(i=0;i<cnt+1;i++)
236                         printf("%02x ",buf[i]);
237                 printf("| (%d)\n",cnt+1);
238         }
239         buf[cnt+1]='\0';
240
241         /* return if type is go */
242
243         if(type==TXRX_TYPE_GO)
244                 return 0;
245
246         /* check/strip return code if type is cmd */
247
248         if(type==TXRX_TYPE_CMD) {
249                 ret=strlen(CMD_SUCCESS);
250                 if(!strncmp(buf,CMD_SUCCESS,ret)) {
251                         for(i=ret;i<cnt;i++)
252                                 buf[i-ret]=buf[i];
253                         buf[cnt]='\0';
254                 }
255                 else {
256                         printf("txrx bad return code!\n");
257                         return -1;
258                 }
259         }
260
261         return cnt;
262 }
263
264 int bl_init(t_lpc *lpc) {
265
266         char buf[BUFSIZE];
267         int len;
268
269         /* auto baud sequence */
270         buf[0]='?';
271         txrx(lpc,buf,1,TXRX_TYPE_BAUD);
272         if(strncmp(buf,"Synchronized\r\n",14)) {
273                 printf("auto baud detection failed\n");
274                 return -1;
275         }
276
277         /* tell bl that we are synchronized (it's allready in buf) */
278         txrx(lpc,buf,14,TXRX_TYPE_SYNC);
279         if(strncmp(buf,"OK\r\n",4)) {
280                 printf("sync failed\n");
281                 return -1;
282         }
283
284         /* tell bl the crystal frequency */
285         len=strlen(lpc->freq)+2;
286         strncpy(buf,lpc->freq,BUFSIZE);
287         buf[len-2]='\r';
288         buf[len-1]='\n';
289         txrx(lpc,buf,len,TXRX_TYPE_SYNC);
290         if(strncmp(buf,"OK\r\n",4)) {
291                 printf("freq set failed\n");
292                 return -1;
293         }
294
295         return 0;
296 }
297
298 int unlock_go(t_lpc *lpc) {
299
300         char buf[BUFSIZE];
301         int ret;
302
303         memcpy(buf,"U 23130\r\n",9);
304         ret=txrx(lpc,buf,9,TXRX_TYPE_CMD);
305
306         return ret;
307 }
308
309 int go(t_lpc *lpc) {
310
311         char buf[BUFSIZE];
312         int ret,len;
313
314         snprintf(buf,BUFSIZE,"G %d A\r\n",lpc->roff);
315         len=strlen(buf);
316         ret=txrx(lpc,buf,len,TXRX_TYPE_GO);
317
318         return ret;
319 }
320
321 int uuencode(u8 *in,u8 *out,int len) {
322
323         out[0]=0x20+len;
324         out[1]=0x20+((in[0]>>2)&0x3f);
325         out[2]=0x20+(((in[0]<<4)|(in[1]>>4))&0x3f);
326         out[3]=0x20+(((in[1]<<2)|(in[2]>>6))&0x3f);
327         out[4]=0x20+(in[2]&0x3f);
328
329         return 0;
330 }
331
332 int write_to_ram(t_lpc *lpc,char *buf,u32 addr,int len) {
333
334         int lcount;
335         u32 checksum;
336         char txrxbuf[BUFSIZE];
337         int count,bcnt;
338         int nlen,slen;
339         int i;
340
341         /* check length */
342         if(len%4) {
343                 printf("ram write: not a multiple of 4\n");
344                 return -1;
345         }
346
347         /* make it a multiple of 3 (reason: uuencode) */
348         nlen=(!(len%3))?len:((len/3+1)*3);
349         if(nlen>BUFSIZE) {
350                 printf("ram write: too much data\n");
351                 return -1;
352         }
353         for(i=len;i<nlen;i++) buf[i]=0;
354
355         /* prepare addr */
356         addr+=lpc->roff;
357
358         /* prepare write command */
359         if(lpc->info&VERBOSE)
360                 printf("writing 0x%02x bytes to 0x%08x\n",len,addr);
361         snprintf(txrxbuf,BUFSIZE,"W %d %d\r\n",addr,len);
362         slen=strlen(txrxbuf);
363
364         /* send command and check return code */
365         txrx(lpc,txrxbuf,slen,TXRX_TYPE_CMD);
366
367         /* send data */
368         lcount=0;
369         bcnt=0;
370         count=0;
371         checksum=0;
372         while(bcnt<nlen) {
373
374                 /* uuencode / prepare data bytes */
375                 uuencode((u8 *)(buf+bcnt),(u8 *)(txrxbuf),
376                          (bcnt==nlen-3)?(len%3?len%3:3):3);
377                 txrxbuf[5]='\r';
378                 txrxbuf[6]='\n';
379
380                 /* checksum */
381                 checksum+=((u8)buf[bcnt]+(u8)buf[bcnt+1]+(u8)buf[bcnt+2]);
382
383                 /* send a data line */
384                 txrx(lpc,txrxbuf,7,TXRX_TYPE_DATA);
385
386                 /* increase counters */
387                 lcount+=1;
388                 bcnt+=3;
389                 count+=3;
390
391                 /* checksum */
392                 if((!(lcount%20))|(bcnt==nlen)) {
393                         /* send backtick */
394                         memcpy(txrxbuf,"`\r\n",3);
395                         //txrx(lpc,txrxbuf,3,TXRX_TYPE_DATA);
396                         /* send checksum */
397                         snprintf(txrxbuf,BUFSIZE,"%d\r\n",checksum);
398                         slen=strlen(txrxbuf);
399                         txrx(lpc,txrxbuf,slen,TXRX_TYPE_CKSM);
400                         if(!strncmp(txrxbuf,"RESE",4)) {
401                                 read(lpc->sfd,txrxbuf+4,4);
402                                 printf("ram write: resending ...\n");
403                                 bcnt-=count;
404                         }
405                         if(strncmp(txrxbuf,"OK\r\n",4)) {
406                                 printf("ram write: bad response\n");
407                                 return -1;
408                         }
409                         /* reset checksum & counter */
410                         checksum=0;
411                         count=0;
412                 }
413
414         }
415
416         return 0;
417 }
418
419 int firmware_to_ram(t_lpc *lpc) {
420
421         char buf[BUFSIZE];
422         u32 addr,len,type;
423         int ret,temp;
424
425         /* read a line */
426         ret=1;
427         while(ret) {
428                 /* sync line */
429                 ret=read(lpc->fwfd,buf,1);
430                 switch(buf[0]) {
431                         case '\r':
432                                 continue;
433                         case '\n':
434                                 continue;
435                         case ':':
436                                 /* start code */
437                                 break;
438                         default:
439                                 printf("fw to ram: no ihex format\n");
440                                 return -1;
441                 }
442                 /* read len */
443                 ret=read(lpc->fwfd,buf,2);
444                 sscanf(buf,"%02x",&len);
445                 /* read addr */
446                 ret=read(lpc->fwfd,buf,4);
447                 sscanf(buf,"%04x",&addr);
448                 /* read type */
449                 ret=read(lpc->fwfd,buf,2);
450                 sscanf(buf,"%02x",&type);
451                 /* successfull return if type is end of file */
452                 if(type==0x01)
453                         return 0;
454                 /* read data (and cksum) */
455                 ret=read(lpc->fwfd,buf,2*(len+1));
456                 if(ret!=(2*(len+1))) {
457                         printf("fw to ram: data missing\n");
458                                 return -1;
459                 }
460                 for(ret=0;ret<len;ret++) {
461                         sscanf(buf+2*ret,"%02x",&temp);
462                         buf[ret]=temp;
463                 }
464                 /* act according to type */
465                 switch(type) {
466                         //case 0x03:
467                         //      /* get cs and ip */
468                         //      break;
469                         case 0x00:
470                                 if(len%4) {
471                                         printf("fw to ram: invalid len\n");
472                                         return -1;
473                                 }
474                                 write_to_ram(lpc,buf,addr,len);
475                                 break;
476                         case 0x04:
477                                 lpc->roff=((buf[0]<<24)|(buf[1]<<16));
478                                 break;
479                         default:
480                                 printf("fw to ram: unknown type %02x\n",type);
481                                 return -1;
482                 }
483         }
484
485         return 0;
486 }
487
488 int main(int argc,char **argv) {
489
490         t_lpc lpc;
491         int i;
492         int ret;
493
494         /*
495          * initial ... 
496          */
497
498         memset(&lpc,0,sizeof(t_lpc));
499         strncpy(lpc.freq,CRYSTFREQ,7);
500         lpc.roff=RAMOFFSET;
501
502         /* parse argv */
503
504         for(i=1;i<argc;i++) {
505
506                 if(argv[i][0]!='-') {
507                         usage();
508                         return -1;
509                 }
510
511                 switch(argv[i][1]) {
512                         case 'd':
513                                 strncpy(lpc.sdev,argv[++i],127);
514                                 break;
515                         case 'f':
516                                 strncpy(lpc.fwfile,argv[++i],127);
517                                 lpc.info|=FIRMWARE;
518                                 break;
519                         case 'v':
520                                 lpc.info|=VERBOSE;
521                                 break;
522                         case 'c':
523                                 strncpy(lpc.freq,argv[++i],7);
524                                 break;
525                         default:
526                                 usage();
527                                 return -1;
528                 }
529
530         }
531
532         /* open serial port */
533         if(open_serial_device(&lpc)<0)
534                 goto end;
535
536         /* boot loader init */
537         printf("boot loader init ...\n");
538         if(bl_init(&lpc)<0)
539                 return -1;
540
541         /* quit if there is no hex file to process */
542         if(!(lpc.info&FIRMWARE)) {
543                 printf("no firmware -> aborting\n");
544                 goto end;
545         }
546
547         /* open firmware file */
548         if(open_firmware(&lpc)<0)
549                 goto end;
550
551         /* parse intel hex file and write to ram */
552         printf("write firmware to ram ...\n");
553         firmware_to_ram(&lpc);
554
555         /* unlock go cmd */
556         printf("unlock go command ...\n");
557         unlock_go(&lpc);
558
559         /* go! */
560         printf("go ...\n");
561         ret=go(&lpc);
562
563 end:
564         close(lpc.sfd);
565         close(lpc.fwfd);
566
567         return 0;
568 }
569