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