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