5cc15eddf34f1936081f4639903b47a184efbd35
[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_BAUD          0x01
23 #define TXRX_TYPE_SYNC          0x02
24 #define TXRX_TYPE_CMD           0x03
25 #define TXRX_TYPE_DATA          0x04
26 #define TXRX_TYPE_CKSM          0x05
27
28 #define CMD_SUCCESS             "0\r\n"
29 #define INVALID_COMMAND         "1\r\n"
30 #define SRC_ADDR_ERROR          "2\r\n"
31 #define DST_ADDR_ERROR          "3\r\n"
32 #define SRC_ADDR_NOT_MAPPED     "4\r\n"
33 #define DST_ADDR_NOT_MAPPED     "5\r\n"
34 #define COUNT_ERROR             "6\r\n"
35 #define COMPARE_ERROR           "10\r\n"
36 #define BUSY                    "11\r\n"
37 #define PARAM_ERROR             "12\r\n"
38 #define ADDR_ERROR              "13\r\n"
39 #define ADDR_NOT_MAPPED         "14\r\n"
40 #define CMD_LOCKED              "15\r\n"
41 #define INVALID_CODE            "16\r\n"
42 #define INVALID_BAUD_RATE       "17\r\n"
43 #define INVALID_STOP_BIT        "18\r\n"
44
45 #define CRYSTFREQ               "10000"
46 #define RAMOFFSET               0x40000200
47
48 #define BUFSIZE                 128
49
50 typedef unsigned char u8;
51 typedef unsigned short u16;
52 typedef unsigned int u32;
53
54 typedef struct s_lpc {
55         int sfd;                /* serial fd */
56         char sdev[128];         /* seriel device */
57         int fwfd;               /* fimrware fd */
58         char fwfile[128];       /* firmware file */
59         u8 info;                /* info/mode */
60         char freq[8];           /* frequency */
61         u32 hoff;               /* start addr of ihex file */
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,B9600);
97         cfsetospeed(&term,B9600);
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         int ret;
135         char buf[BUFSIZE];
136
137         /* open firmware file */
138
139         lpc->fwfd=open(lpc->fwfile,O_RDONLY);
140
141         if(lpc->fwfd<0)
142                 perror("fw open");
143
144         /* read hex file offset */
145
146         ret=read(lpc->fwfd,buf,7);
147         if(buf[0]!=':') {
148                 printf("fw open: not an intel hex file?\n");
149                 return -1;
150         }
151         sscanf(buf+3,"%04x",&(lpc->hoff));
152         lseek(lpc->fwfd,0,SEEK_SET);
153
154         return lpc->fwfd;
155 }
156
157 int txrx(t_lpc *lpc,char *buf,int len,u8 type) {
158
159         int ret,cnt;
160         int i;
161
162         /* write */
163
164         if(lpc->info&VERBOSE)
165                 printf("  >> ");
166         cnt=0;
167         while(len) {
168                 ret=write(lpc->sfd,buf+cnt,len);
169                 if(ret<0) {
170                         perror("txrx write");
171                         return ret;
172                 }
173                 if(lpc->info&VERBOSE)
174                         for(i=0;i<ret;i++)
175                                 printf("%c",
176                                        ((buf[cnt+i]>0x19)&(buf[cnt+i]<0x7f))?
177                                        buf[cnt+i]:'.');
178                 len-=ret;
179                 cnt+=ret;
180         }
181         if(lpc->info&VERBOSE) {
182                 printf(" | ");
183                 for(i=0;i<cnt;i++)
184                         printf("%02x ",buf[i]);
185                 printf("| (%d)\n",cnt);
186         }
187
188         /* cut the echo if not of type auto baud */
189
190         if(type!=TXRX_TYPE_BAUD) {
191                 while(cnt) {
192                         ret=read(lpc->sfd,buf,cnt);
193                         if(ret<0) {
194                                 perror("txrx echo cut");
195                                 return ret;
196                         }
197                         cnt-=ret;
198                 }
199         }
200
201         /* return here if type is data */
202
203         if(type==TXRX_TYPE_DATA)
204                 return cnt;
205
206         /* read */
207
208         ret=read(lpc->sfd,buf,1);
209         if(ret<0) {
210                 perror("txrx read (first byte)");
211                 return ret;
212         }
213                 
214         switch(buf[0]) {
215                 case 'S':
216                         cnt=13;
217                         break;
218                 case 'O':
219                         cnt=3;
220                         break;
221                 case 'R':
222                         cnt=7;
223                         break;
224                 case '0':
225                         cnt=2;
226                         break;
227                 default:
228                         printf("txrx read: bad return byte '%02x'\n",buf[0]);
229                         break;
230         }
231
232         ret=1;
233         i=cnt;
234         while(i) {
235                 ret=read(lpc->sfd,buf+1+cnt-i,i);
236                 if(ret<0) {
237                         perror("txrx read (next bytes)");
238                         return ret;
239                 }
240                 i-=ret;
241         }
242         if(lpc->info&VERBOSE) {
243                 printf("  << ");
244                 for(i=0;i<cnt+1;i++)
245                         printf("%c",((buf[i]>0x19)&(buf[i]<0x7f))?
246                                     buf[i]:'.');
247                 printf(" | ");
248                 for(i=0;i<cnt+1;i++)
249                         printf("%02x ",buf[i]);
250                 printf("| (%d)\n",cnt+1);
251         }
252         buf[cnt+1]='\0';
253
254         /* check/strip return code if type is cmd */
255
256         if(type==TXRX_TYPE_CMD) {
257                 ret=strlen(CMD_SUCCESS);
258                 if(!strncmp(buf,CMD_SUCCESS,ret)) {
259                         for(i=ret;i<cnt;i++)
260                                 buf[i-ret]=buf[i];
261                         buf[cnt]='\0';
262                 }
263                 else {
264                         printf("txrx bad return code!\n");
265                         return -1;
266                 }
267         }
268
269         return cnt;
270 }
271
272 int bl_init(t_lpc *lpc) {
273
274         char buf[BUFSIZE];
275         int len;
276
277         /* auto baud sequence */
278         buf[0]='?';
279         txrx(lpc,buf,1,TXRX_TYPE_BAUD);
280         if(strncmp(buf,"Synchronized\r\n",14)) {
281                 printf("auto baud detection failed\n");
282                 return -1;
283         }
284
285         /* tell bl that we are synchronized (it's allready in buf) */
286         txrx(lpc,buf,14,TXRX_TYPE_SYNC);
287         if(strncmp(buf,"OK\r\n",4)) {
288                 printf("sync failed\n");
289                 return -1;
290         }
291
292         /* tell bl the crystal frequency */
293         len=strlen(lpc->freq)+2;
294         strncpy(buf,lpc->freq,BUFSIZE);
295         buf[len-2]='\r';
296         buf[len-1]='\n';
297         txrx(lpc,buf,len,TXRX_TYPE_SYNC);
298         if(strncmp(buf,"OK\r\n",4)) {
299                 printf("freq set failed\n");
300                 return -1;
301         }
302
303         return 0;
304 }
305
306 int unlock_go(t_lpc *lpc) {
307
308         char buf[BUFSIZE];
309         int ret;
310
311         memcpy(buf,"U 23130\r\n",9);
312         ret=txrx(lpc,buf,9,TXRX_TYPE_CMD);
313
314         return ret;
315 }
316
317 int go(t_lpc *lpc) {
318
319         char buf[BUFSIZE];
320         int ret,len;
321
322         snprintf(buf,BUFSIZE,"G %d A\r\n",lpc->roff);
323         len=strlen(buf);
324         ret=txrx(lpc,buf,len,TXRX_TYPE_CMD);
325
326         return ret;
327 }
328
329 int uuencode(u8 *in,u8 *out,int len) {
330
331         out[0]=0x20+len;
332         out[1]=0x20+((in[0]>>2)&0x3f);
333         out[2]=0x20+(((in[0]<<4)|(in[1]>>4))&0x3f);
334         out[3]=0x20+(((in[1]<<2)|(in[2]>>6))&0x3f);
335         out[4]=0x20+(in[2]&0x3f);
336
337         return 0;
338 }
339
340 int write_to_ram(t_lpc *lpc,char *buf,u32 addr,int len) {
341
342         int lcount;
343         u32 checksum;
344         char txrxbuf[BUFSIZE];
345         int count,bcnt;
346         int nlen,slen;
347         int i;
348
349         /* check length */
350         if(len%4) {
351                 printf("ram write: not a multiple of 4\n");
352                 return -1;
353         }
354
355         /* make it a multiple of 3 (reason: uuencode) */
356         nlen=(!(len%3))?len:((len/3+1)*3);
357         if(nlen>BUFSIZE) {
358                 printf("ram write: too much data\n");
359                 return -1;
360         }
361         for(i=len;i<nlen;i++) buf[i]=0;
362
363         /* prepare addr */
364         addr+=(lpc->roff-lpc->hoff);
365
366         /* prepare write command */
367         if(lpc->info&VERBOSE)
368                 printf("writing 0x%02x bytes to 0x%08x\n",len,addr);
369         snprintf(txrxbuf,BUFSIZE,"W %d %d\r\n",addr,len);
370         slen=strlen(txrxbuf);
371
372         /* send command and check return code */
373         txrx(lpc,txrxbuf,slen,TXRX_TYPE_CMD);
374
375         /* send data */
376         lcount=0;
377         bcnt=0;
378         count=0;
379         checksum=0;
380         while(bcnt<nlen) {
381
382                 /* uuencode / prepare data bytes */
383                 uuencode((u8 *)(buf+bcnt),(u8 *)(txrxbuf),
384                          (bcnt==nlen-3)?(len%3?len%3:3):3);
385                 txrxbuf[5]='\r';
386                 txrxbuf[6]='\n';
387
388                 /* checksum */
389                 checksum+=((u8)buf[bcnt]+(u8)buf[bcnt+1]+(u8)buf[bcnt+2]);
390
391                 /* send a data line */
392                 txrx(lpc,txrxbuf,7,TXRX_TYPE_DATA);
393
394                 /* increase counters */
395                 lcount+=1;
396                 bcnt+=3;
397                 count+=3;
398
399                 /* checksum */
400                 if((!(lcount%20))|(bcnt==nlen)) {
401                         /* send backtick */
402                         memcpy(txrxbuf,"`\r\n",3);
403                         //txrx(lpc,txrxbuf,3,TXRX_TYPE_DATA);
404                         /* send checksum */
405                         snprintf(txrxbuf,BUFSIZE,"%d\r\n",checksum);
406                         slen=strlen(txrxbuf);
407                         txrx(lpc,txrxbuf,slen,TXRX_TYPE_CKSM);
408                         if(!strncmp(txrxbuf,"RESE",4)) {
409                                 read(lpc->sfd,txrxbuf+4,4);
410                                 printf("ram write: resending ...\n");
411                                 bcnt-=count;
412                         }
413                         if(strncmp(txrxbuf,"OK\r\n",4)) {
414                                 printf("ram write: bad response\n");
415                                 return -1;
416                         }
417                         /* reset checksum & counter */
418                         checksum=0;
419                         count=0;
420                 }
421
422         }
423
424         return 0;
425 }
426
427 int firmware_to_ram(t_lpc *lpc) {
428
429         char buf[BUFSIZE];
430         u32 addr,len,type;
431         int ret,temp;
432
433         /* read a line */
434         ret=1;
435         while(ret) {
436                 /* sync line */
437                 ret=read(lpc->fwfd,buf,1);
438                 switch(buf[0]) {
439                         case '\r':
440                                 continue;
441                         case '\n':
442                                 continue;
443                         case ':':
444                                 /* start code */
445                                 break;
446                         default:
447                                 printf("fw to ram: no ihex format\n");
448                                 return -1;
449                 }
450                 /* read len */
451                 ret=read(lpc->fwfd,buf,2);
452                 sscanf(buf,"%02x",&len);
453                 if(len%4) {
454                         printf("fw to ram: len not a multiple of 4\n");
455                         return -1;
456                 }
457                 /* read addr */
458                 ret=read(lpc->fwfd,buf,4);
459                 sscanf(buf,"%04x",&addr);
460                 /* read type */
461                 ret=read(lpc->fwfd,buf,2);
462                 sscanf(buf,"%02x",&type);
463                 /* successfull return if type is end of file */
464                 if(type==0x01)
465                         return 0;
466                 /* read data (and cksum) */
467                 ret=read(lpc->fwfd,buf,2*(len+1));
468                 if(ret!=(2*(len+1))) {
469                         printf("fw to ram: data missing\n");
470                                 return -1;
471                 }
472                 for(ret=0;ret<len;ret++) {
473                         sscanf(buf+2*ret,"%02x",&temp);
474                         buf[ret]=temp;
475                 }
476                 /* act according to type */
477                 switch(type) {
478                         //case 0x03:
479                         //      /* get cs and ip */
480                         //      break;
481                         case 0x00:
482                                 write_to_ram(lpc,buf,addr,len);
483                                 break;
484                         case 0x01:
485                                 write_to_ram(lpc,buf,addr,len);
486                                 break;
487                         default:
488                                 printf("fw to ram: unknown type %02x\n",type);
489                                 return -1;
490                 }
491         }
492
493         return 0;
494 }
495
496 int main(int argc,char **argv) {
497
498         t_lpc lpc;
499         int i;
500         u8 buf[BUFSIZE];
501         int ret;
502
503         /*
504          * initial ... 
505          */
506
507         memset(&lpc,0,sizeof(t_lpc));
508         strncpy(lpc.freq,CRYSTFREQ,7);
509         lpc.roff=RAMOFFSET;
510
511         /* parse argv */
512
513         for(i=1;i<argc;i++) {
514
515                 if(argv[i][0]!='-') {
516                         usage();
517                         return -1;
518                 }
519
520                 switch(argv[i][1]) {
521                         case 'd':
522                                 strncpy(lpc.sdev,argv[++i],127);
523                                 break;
524                         case 'f':
525                                 strncpy(lpc.fwfile,argv[++i],127);
526                                 lpc.info|=FIRMWARE;
527                                 break;
528                         case 'v':
529                                 lpc.info|=VERBOSE;
530                                 break;
531                         case 'c':
532                                 strncpy(lpc.freq,argv[++i],7);
533                                 break;
534                         default:
535                                 usage();
536                                 return -1;
537                 }
538
539         }
540
541         /* open serial port */
542         if(open_serial_device(&lpc)<0)
543                 goto end;
544
545         /* boot loader init */
546         printf("boot loader init ...\n");
547         if(bl_init(&lpc)<0)
548                 return -1;
549
550         /* quit if there is no hex file to process */
551         if(!(lpc.info&FIRMWARE)) {
552                 printf("no firmware -> aborting\n");
553                 goto end;
554         }
555
556         /* open firmware file */
557         if(open_firmware(&lpc)<0)
558                 goto end;
559
560         /* parse intel hex file and write to ram */
561         printf("write firmware to ram ...\n");
562         firmware_to_ram(&lpc);
563
564         /* unlock go cmd */
565         printf("unlock go command ...\n");
566         unlock_go(&lpc);
567
568         /* go! */
569         printf("go ...\n");
570         go(&lpc);
571
572         printf("\n\n");
573         printf("the above error might be due to the jump!\n");
574         printf("\n\n");
575
576         printf("sleeping for 5 seconds ...\n\n");
577         sleep(5);
578
579         printf("continue listening on the serial port. ctrl+c to quit!\n\n");
580
581         /* testing */
582
583         ret=1;
584         while(ret) {
585                 ret=read(lpc.sfd,buf,BUFSIZE);
586                 printf("\rread %d bytes: ",ret);
587                 for(i=0;i<ret;i++)
588                         printf("%02x ",buf[i]);
589         }
590
591 end:
592         close(lpc.sfd);
593         close(lpc.fwfd);
594
595         return 0;
596 }
597