Imported Upstream version 2.9.0
[debian/cc1111] / device / examples / ds390 / tinitalk / tinitalk.c
1 /*-------------------------------------------------------------------------
2   tinitalk.c - A tini utility to download files to TINI and talk to it
3   
4   Written By - Johan Knol johan.knol@iduna.nl
5   
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10   
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15   
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19   
20   In other words, you are welcome to use, share and improve this program.
21   You are forbidden to forbid anyone else to use, share and improve
22   what you give them.   Help stamp out software-hoarding!  
23   -------------------------------------------------------------------------*/
24
25 #if defined(_MSC_VER) || defined(__BORLANDC__)
26 #define TINI_PORT "COM2"
27 #else
28 #ifdef linux
29 // must be linux
30 #define TINI_PORT "/dev/ttyS0"
31 #else
32 // could be solaris
33 #define TINI_PORT "/dev/term/a"
34 #endif
35 #endif
36 #define TINI_BAUD 115200
37 #define TINI_ESCAPE_CHAR 0x1b
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <fcntl.h>
44
45 #if !defined( _MSC_VER) && !defined(__BORLANDC__)
46 #include <unistd.h>
47 #include <termios.h>
48 #include <sys/ioctl.h>
49 #else
50 #include <conio.h>
51 #include <windows.h>
52 #define sleep(ms) Sleep((ms*1000))
53 #define usleep(us) Sleep((us/1000))
54 #endif
55
56 #include "tinitalk.h"
57
58 char *programName;
59 char escapeChar = 0;
60 char *port = NULL;
61 int baud = 0, appBaud = 0;
62
63 void
64 Usage ()
65 {
66   fprintf (stderr, "usage: %s <options> command [args] \n", programName);
67   fprintf (stderr, "\nwhere options are:\n");
68   fprintf (stderr, "  <-p port> set the serial port, defaults to %s\n", TINI_PORT);
69   fprintf (stderr, "  <-b baud> set the baud rate, defaults to %d\n", TINI_BAUD);
70   fprintf (stderr, "  <-B baud> set the baud rate for the application\n");
71   fprintf (stderr, "  <-c> connect to tini after command (if any)\n");
72   fprintf (stderr, "  <-e #> to set the escape char\n");
73   fprintf (stderr, "  <-s> to see some examples.\n");
74   fprintf (stderr, "\nand commands are:\n");
75   fprintf (stderr, "  <load file> load a hex file and restart the bootloader\n");
76   fprintf (stderr, "  <execute [file]> load a hex file and/or start the program in bank 1\n");
77   exit (1);
78 }
79
80 void
81 Examples ()
82 {
83   printf ("\n");
84   printf ("%s -p /dev/ttyS1\n", programName);
85   printf ("    now you are talking to the bootloader through serial-1\n");
86   printf ("%s -b 19200\n", programName);
87   printf ("    now you are talking to the bootloader at 19200 baud\n");
88   printf ("%s load tini.hex\n", programName);
89   printf ("    load tini.hex\n");
90   printf ("%s -c load tini.hex\n", programName);
91   printf ("    after loading tini.hex you are talking to the bootloader\n");
92   printf ("%s execute tini.hex\n", programName);
93   printf ("    load tini.hex and start the program in bank 1\n");
94   printf ("%s -c execute tini.hex\n", programName);
95   printf ("    after loading the file you are talking to the (restarted)\n");
96   printf ("    program in bank 1\n");
97   printf ("%s execute\n", programName);
98   printf ("    now the program in bank 1 is restarted.\n");
99   printf ("%s -c execute\n", programName);
100   printf ("    now you are talking to the (restarted) program in bank 1\n");
101   printf ("%s -b 115200 -B 9600 -c execute tini.hex\n", programName);
102   printf ("    download tini.hex at 115200 baud, but talk the program at 9600 baud\n");
103   exit (0);
104 }
105
106 int
107 main (int argc, char **argv)
108 {
109   int connect = 0;
110   char command[64];
111
112   int arg = 1;
113
114   //programName=argv[0];
115   programName = "tinitalk";
116
117   // process options
118   while (arg < argc && argv[arg][0] == '-')
119     {
120
121       // no arguments required
122       if (argv[arg][1] == 'c')
123         {
124           connect = 1;
125           arg++;
126           continue;
127         }
128       else if (argv[arg][1] == 's')
129         {
130           Examples ();
131           // will not return
132         }
133       // argument required
134       if (arg >= (argc - 1))
135         {
136           Usage ();
137         }
138       switch (argv[arg][1])
139         {
140         case 'p':
141           port = argv[arg + 1];
142           break;
143         case 'b':
144           baud = atoi (argv[arg + 1]);
145           break;
146         case 'B':
147           appBaud = atoi (argv[arg + 1]);
148           break;
149         case 'e':
150           escapeChar = atoi (argv[arg + 1]);
151           break;
152         default:
153           Usage ();
154         }
155       arg += 2;
156     }
157
158   if (port == NULL)
159     {
160       if ((port = getenv ("TINI_PORT")) == NULL)
161         {
162           port = TINI_PORT;
163         }
164     }
165   if (baud == 0)
166     {
167       if (getenv ("TINI_BAUD"))
168         {
169           baud = atoi (getenv ("TINI_BAUD"));
170         }
171       else
172         {
173           baud = TINI_BAUD;
174         }
175     }
176   if (escapeChar == 0)
177     {
178       if (getenv ("TINI_ESCAPE_CHAR"))
179         {
180           escapeChar = atoi (getenv ("TINI_ESCAPE_CHAR"));
181         }
182       else
183         {
184           escapeChar = TINI_ESCAPE_CHAR;
185         }
186     }
187   if (appBaud == 0)
188     {
189       appBaud = baud;
190     }
191   if (!TiniOpen (port, baud))
192     {
193       exit (1);
194     }
195   // process commands
196   while (arg < argc)
197     {
198       if (strcmp (argv[arg], "load") == 0)
199         {
200           // argument required
201           if (arg >= (argc - 1))
202             {
203               Usage ();
204             }
205           if (LoadHexFile (argv[arg + 1]))
206             {
207               TiniReset (1);
208               if (connect)
209                 {
210                   TiniConnect (baud);
211                 }
212             }
213           exit (0);
214         }
215       if (strcmp (argv[arg], "execute") == 0)
216         {
217           // argument supplied?
218           if (arg < (argc - 1))
219             {
220               if (!LoadHexFile (argv[arg + 1]))
221                 {
222                   exit (0);
223                 }
224             }
225           TiniReset (0);
226           if (connect)
227             {
228               TiniConnect (appBaud);
229             }
230           exit (0);
231         }
232       // unsupported command
233       Usage ();
234     }
235
236   // no commands, just connect
237
238   // on my linux box, DTR is always set after opening the port, so:
239   // reset the bootloader
240
241   strcpy (command, "r");
242
243   while (1)
244     {
245       switch (tolower (command[0]))
246         {
247         case '?':
248         case '\n':
249         case 'h':
250           printf ("\n");
251           printf ("r - reset, start bootloader and connect to TINI\n");
252           printf ("e - reset, start program in bank 1 and connect to TINI\n");
253           printf ("c - connect to TINI.\n");
254           printf ("l - load file.\n");
255           printf ("s - save file.\n");
256           printf ("q - quit.\n");
257           break;
258         case 'e':
259           TiniReset (0);
260           TiniConnect (appBaud);
261           break;
262         case 'r':
263           TiniReset (1);
264           TiniConnect (baud);
265           break;
266         case 'c':
267           // leave it as it was
268           TiniConnect (0);
269           break;
270         case 'l':
271           {
272             char fileName[FILENAME_MAX] = "";
273             printf ("Enter filename: ");
274             fflush (stdout);
275             fgets (fileName, FILENAME_MAX, stdin);
276             // remove the EOL character
277             fileName[strlen (fileName) - 1] = 0;
278             LoadHexFile (fileName);
279           }
280           break;
281         case 's':
282           printf ("Command \"%c\" not implemented yet.\n", command[0]);
283           break;
284         case 'q':
285           return 0;
286           break;
287         default:
288           printf ("Unknown command: \"%c\".\n", command[0]);
289           break;
290         }
291       printf ("\n<%s> ", programName);
292       fflush (stdout);
293 #if defined(_MSC_VER) || defined(__BORLANDC__)
294       // don't know why
295       getch ();
296 #endif
297       fgets (command, 64, stdin);
298     }
299   return 0;
300 }
301
302 int
303 LoadHexFile (char *path)
304 {
305   FILE *hexFile;
306   char hexLine[256];
307   int bank = 0;
308   int line = 0, type;
309   char tempString[16];
310   char c, ctrlC = 0x03;
311   int bytesLoaded = 0, progress = 0;
312   char banksZapped[8] =
313   {0, 0, 0, 0, 0, 0, 0, 0};
314   unsigned int address, bytes, i;
315   unsigned int checksum, chk;
316
317   if ((hexFile = fopen (path, "r")) == NULL)
318     {
319       perror (path);
320       return 0;
321     }
322   TiniFlush ();
323
324   while (fgets (hexLine, 256, hexFile))
325     {
326
327       if (TiniRead (&c, 1) == 1)
328         {
329           // show error messages from TINI
330           printf ("\n");
331           do
332             {
333               putchar (c);
334             }
335           while (TiniRead (&c, 1) == 1);
336
337           printf ("\nInterrupted by loader.\n");
338           return 0;
339         }
340       line++;
341
342       if (hexLine[0] != ':' ||
343           sscanf (&hexLine[1], "%02x", &bytes) != 1 ||
344           sscanf (&hexLine[3], "%04x", &address) != 1 ||
345           sscanf (&hexLine[7], "%02x", &type) != 1)
346         {
347           printf ("Invalid ihx record: \"%s\"\n", hexLine);
348           TiniReset (1);
349           return 0;
350         }
351       // make sure line ends with '\r' or TINI won't swallow it
352       hexLine[strlen (hexLine) - 1] = '\r';
353
354       address += bank << 16;
355
356       // test checksum
357       checksum = 0;
358       for (i = 0; i < bytes + 5; i++)
359         {
360           sscanf (&hexLine[i * 2 + 1], "%02x", &chk);
361           checksum += chk;
362         }
363       if (checksum & 0xff)
364         {
365           printf ("\nChecksum error at %06x (0x%02x!=0) in line: %d\n",
366                   address, checksum&0xff, line);
367           TiniReset (1);
368           return 0;
369         }
370       if (type == 4)
371         {
372           // new Hex386 record
373           sscanf (&hexLine[9], "%04x", &bank);
374           address = (address & 0xffff) + (bank << 16);
375           // just for safety
376           if (bank == 0)
377             {
378               printf ("==> No overwrite of bank 0 <==\n");
379               return 0;
380             }
381           if (0)
382             {
383               // interupt loader
384               TiniWriteAndWait (&ctrlC, 1, '>');
385             }
386           else
387             {
388               // reset TINI
389               TiniReset (1);
390             }
391           if (!banksZapped[bank])
392             {
393               // zap bank
394               sprintf (tempString, "z%d\r", bank);
395               TiniWriteAndWait (tempString, 3, '?');
396               TiniWriteAndWait ("y", 1, '\n');
397               printf ("[Zapping bank %d]\n", bank);
398               TiniWait ('>');
399               banksZapped[bank] = 1;
400               // start loader
401               //printf ("[Starting loader]\n");
402             }
403           TiniWriteAndWait ("l\r", 2, '\n');
404           printf ("[Loading bank %d]\n", bank);
405         }
406       if (bank > 0)
407         {
408           if ((type == 0) && (1 || ((bytesLoaded / 1024) > progress)))
409             {
410               progress = bytesLoaded / 1024;
411               printf ("[%06x: sent %d bytes]\r", address, bytesLoaded);
412               fflush (stdout);
413             }
414           bytesLoaded += bytes;
415
416           //printf ("data: %s\n", hexLine);
417           TiniWrite ("            ", 12);
418           TiniWrite (hexLine, strlen (hexLine));
419           TiniDrain ();
420         }
421       else
422         {
423           //printf ("skip: %s\n", hexLine);
424         }
425     }
426   TiniWriteAndWait ("\r", 1, '>');
427   printf ("\n[Load succesfull]\n");
428   fclose (hexFile);
429   return 1;
430 }
431
432 int
433 SaveFile (char *path)
434 {
435   printf ("Saving file: %s\n", path);
436   return 1;
437 }
438
439 /*
440    this is the io part 
441  */
442
443 #if defined(_MSC_VER) || defined(__BORLANDC__)
444 HANDLE tiniHandle;
445 DCB tiniDcb;
446 #else
447 static int tini_fd;
448 static int tini_status;
449 static struct termios tini_options;
450 #endif
451
452 static int initflag = 0;
453
454 int
455 TiniOpen (char *port, int baud)
456 {
457
458   if (initflag)
459     {
460       return 1;
461     }
462   printf ("[Opening \"%s\" at \"%d\" baud, escape is 0x%02x]\n",
463           port, baud, escapeChar);
464
465 #if defined(_MSC_VER) || defined(__BORLANDC__)
466   if ((tiniHandle = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
467                              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) ==
468       INVALID_HANDLE_VALUE)
469 #else
470   if ((tini_fd = open (port, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
471 #endif
472     {
473       fprintf (stderr, "%s: unable to open port %s - ",
474                "TiniOpen", port);
475       perror("");
476       return 0;
477     }
478   // configure the serial port
479 #if defined(_MSC_VER) || defined(__BORLANDC__)
480   tiniDcb.DCBlength = sizeof (DCB);
481   if (GetCommState (tiniHandle, &tiniDcb) != TRUE)
482     {
483       fprintf (stderr, "%s: unable to query port %s\n",
484                "TiniOpen", port);
485       TiniClose ();
486       return 0;
487     }
488   tiniDcb.StopBits = 0;
489   tiniDcb.ByteSize = 8;
490   tiniDcb.Parity = 0;
491   tiniDcb.fDtrControl = DTR_CONTROL_DISABLE;
492   tiniDcb.fRtsControl = RTS_CONTROL_DISABLE;
493   tiniDcb.fOutxCtsFlow = FALSE;
494   tiniDcb.fOutX = FALSE;
495   tiniDcb.fInX = FALSE;
496   if (SetCommState (tiniHandle, &tiniDcb) != TRUE)
497     {
498       fprintf (stderr, "%s: unable to configure port %s\n",
499                "TiniOpen", port);
500       TiniClose ();
501       return 0;
502     }
503   // reset DTR
504   EscapeCommFunction (tiniHandle, CLRDTR);
505 #else
506   if (ioctl (tini_fd, TIOCMGET, &tini_status))
507     {
508       perror ("0: ioctl(tini_fd, TIOCMGET, &tini_status) - ");
509       TiniClose ();
510       return 0;
511     }
512   // reset DTR
513   tini_status &= ~TIOCM_DTR;
514   if (ioctl (tini_fd, TIOCMSET, &tini_status))
515     {
516       perror ("2: ioctl(tini_fd, TIOCMSET, &tini_status) - ");
517       TiniClose ();
518       return 0;
519     }
520   tcgetattr (tini_fd, &tini_options);
521   tini_options.c_cflag |= (CLOCAL | CREAD);
522   tini_options.c_lflag &= ~(ISIG | ICANON | ECHO);
523   tini_options.c_iflag |= (IGNCR);
524   tini_options.c_cc[VMIN] = 0;
525   tini_options.c_cc[VTIME] = 0;
526   tcsetattr (tini_fd, TCSANOW, &tini_options);
527 #endif
528
529   if (!TiniBaudRate (baud))
530     {
531       TiniClose ();
532       return 0;
533     }
534   initflag = 1;
535   return 1;
536 }
537
538 #if defined(_MSC_VER) || defined(__BORLANDC__)
539 #define B9600 CBR_9600
540 #define B19200 CBR_19200
541 #define B38400 CBR_38400
542 #define B57600 CBR_57600
543 #define B115200 CBR_115200
544 #endif
545
546 int
547 TiniBaudRate (int baud)
548 {
549   int baudB;
550
551   switch (baud)
552     {
553     case 9600:
554       baudB = B9600;
555       break;
556     case 19200:
557       baudB = B19200;
558       break;
559     case 38400:
560       baudB = B38400;
561       break;
562     case 57600:
563       baudB = B57600;
564       break;
565     case 115200:
566       baudB = B115200;
567       break;
568     default:
569       fprintf (stderr, "%s: illegal baudrate: \"%d\"\n", programName, baud);
570       return 0;
571     }
572
573 #if defined(_MSC_VER) || defined(__BORLANDC__)
574   tiniDcb.BaudRate = baudB;
575   SetCommState (tiniHandle, &tiniDcb);
576 #else
577   cfsetispeed (&tini_options, baudB);
578   cfsetospeed (&tini_options, baudB);
579   tcsetattr (tini_fd, TCSANOW, &tini_options);
580 #endif
581
582   return 1;
583 }
584
585 int
586 TiniReset (int toBootLoader)
587 {
588
589   // set DTR
590 #if defined(_MSC_VER) || defined(__BORLANDC__)
591   EscapeCommFunction (tiniHandle, SETDTR);
592 #else
593   tini_status |= TIOCM_DTR;
594   if (ioctl (tini_fd, TIOCMSET, &tini_status))
595     {
596       perror ("1: ioctl(tini_fd, TIOCMSET, &tini_status) - ");
597       return 0;
598     }
599 #endif
600
601   // wait for 100ms
602   usleep (100000);
603
604   // drain input and output buffers
605   TiniDrain ();
606
607   // reset DTR
608 #if defined(_MSC_VER) || defined(__BORLANDC__)
609   EscapeCommFunction (tiniHandle, CLRDTR);
610 #else
611   tini_status &= ~TIOCM_DTR;
612   if (ioctl (tini_fd, TIOCMSET, &tini_status))
613     {
614       perror ("2: ioctl(tini_fd, TIOCMSET, &tini_status) - ");
615       return 0;
616     }
617 #endif
618
619   // wait for 100ms
620   usleep (100000);
621
622   if (TiniWrite ("\r", 1) != 1)
623     {
624       fprintf (stderr, "TiniReset: couldn't write to tini\n");
625       return 0;
626     }
627   // wait for the bootloader prompt
628   // we should build a timeout here
629   TiniWait ('>');
630
631   if (toBootLoader)
632     {
633       return 1;
634     }
635   TiniWriteAndWait ("E\r", 2, 'E');
636   return 1;
637 }
638
639 #if defined(_MSC_VER) || defined(__BORLANDC__)
640 // read as much character as available, at most n
641 int
642 TiniRead (char *buffer, int n)
643 {
644   int count;
645   int status;
646   COMSTAT tiniComStat;
647
648   ClearCommError (tiniHandle, &status, &tiniComStat);
649   if (tiniComStat.cbInQue < (unsigned int) n)
650     {
651       n = tiniComStat.cbInQue;
652     }
653   ReadFile (tiniHandle, buffer, n, &count, NULL);
654
655   return count;
656 }
657
658 int
659 TiniWrite (char *buffer, int n)
660 {
661   int count;
662   WriteFile (tiniHandle, buffer, n, &count, NULL);
663   return count;
664 }
665 #else
666 int
667 TiniRead (char *buffer, int n)
668 {
669   return read (tini_fd, buffer, n);
670 }
671
672 int
673 TiniWrite (char *buffer, int n)
674 {
675   return write (tini_fd, buffer, n);
676 }
677 #endif
678
679 // wait for the prompChar
680 int
681 TiniWait (char promptChar)
682 {
683   char c;
684   while (1)
685     {
686       switch (TiniRead (&c, 1))
687         {
688         case 0:         // no char available
689           // give up our time slice
690
691           sleep (0);
692           break;
693         case 1:         // one char read
694
695           putchar (c);
696           fflush (stdout);
697           if (c == promptChar)
698             {
699               return 1;
700             }
701           break;
702         default:                // some error
703
704           perror ("TiniWait: ");
705           return 0;
706           break;
707         }
708     }
709 }
710
711 // send the buffer and wait for the promptChar
712 int
713 TiniWriteAndWait (char *buffer, int n, char promptChar)
714 {
715   char bytes = TiniWrite (buffer, n);
716   TiniWait (promptChar);
717   return bytes;
718 }
719
720 // flush input and output buffers (wait for it)
721 void
722 TiniFlush ()
723 {
724 #if defined(_MSC_VER) || defined(__BORLANDC__)
725   FlushFileBuffers (tiniHandle);
726 #else
727   // flush the buffers, isn't there a simpler way?
728   tcsetattr (tini_fd, TCSAFLUSH, &tini_options);
729 #endif
730 }
731
732 // drain input and output buffers (forget it)
733 void
734 TiniDrain ()
735 {
736 #if defined(_MSC_VER) || defined(__BORLANDC__)
737   PurgeComm (tiniHandle, PURGE_TXCLEAR | PURGE_RXCLEAR);
738 #else
739   // drain the buffers, isn't there a simpler way?
740   tcsetattr (tini_fd, TCSADRAIN, &tini_options);
741 #endif
742 }
743
744 #if defined(_MSC_VER) || defined(__BORLANDC__)
745 void
746 TiniConnect (int baud)
747 {
748   char c;
749
750   if (baud)
751     {
752       TiniBaudRate (baud);
753     }
754
755   while (1)
756     {
757       if (TiniRead (&c, 1))
758         {
759           // char from TINI, high priority
760           putchar (c);
761           fflush (stdout);
762         }
763       else if (kbhit ())
764         {
765           // char from console, low priotity
766           if ((c = getch ()) == escapeChar)
767             {
768               // escape from connect?
769               printf ("<ESC>");
770               break;
771             }
772           TiniWrite (&c, 1);
773         }
774       else
775         {
776           // nothing to do, so give up our timeslice
777           sleep (0);
778         }
779     }
780 }
781 #else
782 void
783 TiniConnect (int baud)
784 {
785   struct termios options, consoleOptions;
786   int consoleFlags;
787   char c;
788   int fno;
789
790   if (baud)
791     {
792       TiniBaudRate (baud);
793     }
794
795   // set stdin to nonblocking IO, noecho
796   fno = fileno (stdin);
797   consoleFlags = fcntl (fno, F_GETFL);
798   fcntl (fno, F_SETFL, consoleFlags | O_NONBLOCK);
799
800   tcgetattr (fno, &consoleOptions);
801   options = consoleOptions;
802   options.c_lflag &= ~(ISIG | ICANON | ECHO);
803   tcsetattr (fno, TCSANOW, &options);
804
805   while (1)
806     {
807       if (TiniRead (&c, 1) == 1)
808         {
809           // char from TINI, high priority
810           putchar (c);
811         }
812       else if ((c = getchar ()) != EOF)
813         {
814           // char from console, low priority
815           if (c == escapeChar)
816             {
817               // escape from connect?
818               break;
819             }
820           if (c == '\n')
821             {
822               c = '\r';
823             }
824           TiniWrite (&c, 1);
825         }
826       else
827         {
828           // nothing to do, so give up our timeslice
829           sleep (0);
830         }
831     }
832
833   // reset stdin
834   fcntl (fno, F_SETFL, consoleFlags);
835   tcsetattr (fno, TCSANOW, &consoleOptions);
836 }
837 #endif
838
839 void
840 TiniClose (void)
841 {
842   initflag = 0;
843 #if defined(_MSC_VER) || defined(__BORLANDC__)
844   CloseHandle (tiniHandle);
845 #else
846   close (tini_fd);
847 #endif
848 }