1 /*********************************************************************
2  *
3  *   ISP (In-System Programming) tool for NXP LPC Microcontrollers
4  *
5  * This tool gives access to each of the useful isp commands on LPC
6  * devices. It does not provide wrappers for flashing a device.
7  *
8  *  Copyright (C) 2012 Nathael Pajani <nathael.pajani@nathael.net>
9  *
10  *  This program is free software: you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation, either version 3 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  *********************************************************************/
26 #include <stdlib.h> /* malloc, free */
27 #include <stdio.h>
28 #include <stdint.h>
30 #include <unistd.h> /* open, getopt, close, usleep */
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
35 #include <errno.h>
36 #include <getopt.h>
38 #include <termios.h> /* for serial config */
39 #include <ctype.h>
41 #include <string.h> /* strncmp, strlen */
43 #include "isp_utils.h"
44 #include "isp_commands.h"
46 #define PROG_NAME "LPC ISP"
47 #define VERSION   "1.07"
49 /* short explanation on exit values:
50  *
51  * 0 is OK
52  * 1 is arg error
53  *
54  */
57 void help(char *prog_name)
58 {
59         fprintf(stderr, "-----------------------------------------------------------------------\n");
60         fprintf(stderr, "Usage: %s [options] device [-s | --synchronize]\n" \
61                 "       %s [options] device command [command arguments]\n" \
62                 "  Default baudrate is B115200\n" \
63                 "  <device> is the (host) serial line used to program the device\n" \
64                 "  <command> is one of:\n" \
65                 "  \t unlock, write-to-ram, read-memory, prepare-for-write, copy-ram-to-flash, go, erase,\n" \
66                 "  \t blank-check, read-part-id, read-boot-version, compare and read-uid.\n" \
67                 "  command specific arguments are as follow:\n" \
68                 "  \t unlock \n" \
69                 "  \t write-to-ram address file uuencode : send 'file' to 'address' in ram with or without uuencoding\n" \
70                 "  \t read-memory address count file uuencoded : read 'count' bytes from 'address', store then in 'file' (maybe decode)\n" \
71                 "  \t prepare-for-write first last : prepare sectors from 'first' to 'last' for write operation\n" \
72                 "  \t copy-ram-to-flash flash_addr ram_addr count : copy count bytes (256, 512, 1024 or 4096)\n" \
73                 "  \t     from 'ram_addr' to 'flash_addr'\n" \
74                 "  \t go address mode : execute program at 'address' (> 0x200) in 'mode' ('arm' or 'thumb')\n" \
75                 "  \t erase first last : erase flash starting from 'first' sector up to (including) 'last' sector \n" \
76                 "  \t blank-check first last : check flash starting from 'first' sector to 'last' sector is blank\n" \
77                 "  \t read-part-id \n" \
78                 "  \t read-boot-version \n" \
79                 "  \t compare address1 address2 count : compare count bytes between address1 and address2\n" \
80                 "  \t read-uid \n" \
81                 "  Notes:\n" \
82                 "   - Access to the ISP mode is done by calling this utility once with the synchronize\n" \
83                 "     option and no command. This starts a session. No command can be used before starting\n" \
84                 "     a session, and no other synchronize request must be done once the session is started\n" \
85                 "     unless the target is reseted, which closes the session.\n" \
86                 "   - Echo is turned OFF when starting a session and the command is not available.\n" \
87                 "   - The set-baud-rate command is not available. The SAME baudrate MUST be used for the\n" \
88                 "     whole session. It must be specified on each successive call if the default baudrate\n" \
89                 "     is not used.\n" \
90                 "   - User must issue an 'unlock' command before any of 'copy-ram-to-flash', 'erase',\n" \
91                 "     and 'go' commands.\n" \
92                 "  Available options:\n" \
93                 "  \t -s | --synchronize : Perform synchronization (open session)\n" \
94                 "  \t -b | --baudrate=N : Use this baudrate (does not issue the set-baud-rate command)\n" \
95                 "  \t -t | --trace : turn on trace output of serial communication\n" \
96                 "  \t -h | --help : display this help\n" \
97                 "  \t -v | --version : display version information\n", prog_name, prog_name);
98         fprintf(stderr, "-----------------------------------------------------------------------\n");
99 }
101 #define SERIAL_BAUD  B115200
103 int trace_on = 0;
105 int isp_handle_command(char* cmd, int arg_count, char** args);
107 int main(int argc, char** argv)
109         int baudrate = SERIAL_BAUD;
110         int crystal_freq = 10000;
111         int synchronize = 0;
112         char* isp_serial_device = NULL;
114         /* For "command" handling */
115         char* command = NULL;
116         char** cmd_args = NULL;
117         int nb_cmd_args = 0;
120         /* parameter parsing */
121         while(1) {
122                 int option_index = 0;
123                 int c = 0;
125                 struct option long_options[] = {
126                         {"synchronize", no_argument, 0, 's'},
127                         {"baudrate", required_argument, 0, 'b'},
128                         {"trace", no_argument, 0, 't'},
129                         {"help", no_argument, 0, 'h'},
130                         {"version", no_argument, 0, 'v'},
131                         {0, 0, 0, 0}
132                 };
134                 c = getopt_long(argc, argv, "sb:thv", long_options, &option_index);
136                 /* no more options to parse */
137                 if (c == -1) break;
139                 switch (c) {
140                         /* b, baudrate */
141                         case 'b':
142                                 baudrate = atoi(optarg);
143                                 /* FIXME: validate baudrate */
144                                 break;
146                         /* t, trace */
147                         case 't':
148                                 trace_on = 1;
149                                 break;
151                         /* s, synchronize */
152                         case 's':
153                                 synchronize = 1;
154                                 break;
156                         /* v, version */
157                         case 'v':
158                                 printf("%s Version %s\n", PROG_NAME, VERSION);
159                                 return 0;
160                                 break;
162                         /* h, help */
163                         case 'h':
164                         default:
165                                 help(argv[0]);
166                                 return 0;
167                 }
168         }
170         /* Parse remaining command line arguments (not options). */
172         /* First one should (must) be serial device */
173         if (optind < argc) {
174                 isp_serial_device = argv[optind++];
175                 if (trace_on) {
176                         printf("Serial device : %s\n", isp_serial_device);
177                 }
178         }
179         if (isp_serial_device == NULL) {
180                 printf("No serial device given, exiting\n");
181                 help(argv[0]);
182                 return 0;
183         }
184         if (isp_serial_open(baudrate, isp_serial_device) != 0) {
185                 printf("Serial open failed, unable to initiate serial communication with target.\n");
186                 return -1;
187         }
189         if (synchronize) {
190                 if (optind < argc) {
191                         /* no command can be specified when opening a session */
192                         printf("No command can be specified with -s or --synchronize (Session opening)\n");
193                         printf("NOT SYNCHRONIZED !\n");
194                         return -1;
195                 }
196                 isp_connect(crystal_freq, 0);
197                 isp_serial_close();
198                 return 0;
199         }
201         /* Next one should be "command" (if present) */
202         if (optind < argc) {
203                 command = argv[optind++];
204                 if (trace_on) {
205                         printf("Command : %s\n", command);
206                 }
207         } else {
208                 printf("No command given. use -h or --help for help on available commands.\n");
209                 isp_serial_close();
210                 return -1;
211         }
212         /* And then remaining ones (if any) are command arguments */
213         if (optind < argc) {
214                 nb_cmd_args = argc - optind;
215                 cmd_args = malloc(nb_cmd_args * sizeof(char *));
216                 if (trace_on) {
217                         printf("Command arguments :\n");
218                 }
219                 while (optind < argc) {
220                         static unsigned int idx = 0;
221                         cmd_args[idx++] = argv[optind++];
222                         if (trace_on) {
223                                 printf("%s\n", cmd_args[idx - 1]);
224                         }
225                 }
226         }
228         if (command != NULL)  {
229                 int err = 0;
230                 err = isp_handle_command(command, nb_cmd_args, cmd_args);
231                 if (err >= 0) {
232                         if (trace_on) {
233                                 printf("Command \"%s\" handled OK.\n", command);
234                         }
235                 } else {
236                         printf("Error handling command \"%s\" : %d\n", command, err);
237                 }
238         }
241         if (cmd_args != NULL) {
242                 free(cmd_args);
243         }
244         isp_serial_close();
245         return 0;
248 struct isp_command {
249         int cmd_num;
250         char* name;
251         int nb_args;
252         int (*handler)(int arg_count, char** args);
253 };
256 static struct isp_command isp_cmds_list[] = {
257         {0, "unlock", 0, NULL},
258         {1, "write-to-ram", 3, isp_cmd_write_to_ram},
259         {2, "read-memory", 3, isp_cmd_read_memory},
260         {3, "prepare-for-write", 2, isp_cmd_prepare_for_write},
261         {4, "copy-ram-to-flash", 3, isp_cmd_copy_ram_to_flash},
262         {5, "go", 2, isp_cmd_go},
263         {6, "erase", 2, isp_cmd_erase},
264         {7, "blank-check", 2, isp_cmd_blank_check},
265         {8, "read-part-id", 0, NULL},
266         {9, "read-boot-version", 0, NULL},
267         {10, "compare", 3, isp_cmd_compare},
268         {11, "read-uid", 0, NULL},
269         {-1, NULL, 0, NULL}
270 };
272 void isp_warn_args(int cmd_num, int arg_count, char** args)
274         int i = 0;
275         printf("command \"%s\" needs %d args, got %d.\n",
276                         isp_cmds_list[cmd_num].name,
277                         isp_cmds_list[cmd_num].nb_args, arg_count);
278         for (i=0; i<arg_count; i++) {
279                 printf("\targ[%d] : \"%s\"\n", i, args[i]);
280         }
283 /* Handle one command
284  * Return positive or NULL value when command handling is OK, or negative value otherwise.
285  */
286 int isp_handle_command(char* cmd, int arg_count, char** args)
288         int cmd_found = -1;
289         int ret = 0;
290         int index = 0;
292         if (cmd == NULL) {
293                 printf("isp_handle_command called with no command !\n");
294                 return -1;
295         }
297         while ((cmd_found == -1) && (isp_cmds_list[index].name != NULL)) {
298                 if (strncmp(isp_cmds_list[index].name, cmd, strlen(isp_cmds_list[index].name)) == 0) {
299                         cmd_found = index;
300                         break;
301                 }
302                 index++;
303         }
304         if (cmd_found == -1) {
305                 printf("Unknown command \"%s\", use -h or --help for a list.\n", cmd);
306                 return -2;
307         }
308         if (arg_count != isp_cmds_list[cmd_found].nb_args) {
309                 isp_warn_args(cmd_found, arg_count, args);
310         }
312         switch (isp_cmds_list[cmd_found].cmd_num) {
313                 case 0: /* unlock */
314                         ret = isp_cmd_unlock(0);
315                         break;
316                 case 8: /* read-part-id */
317                         ret = isp_cmd_part_id(0);
318                         break;
319                 case 9: /* read-boot-version */
320                         ret = isp_cmd_boot_version();
321                         break;
322                 case 11: /* read-uid */
323                         ret = isp_cmd_read_uid();
324                         break;
325                 default:
326                         ret = isp_cmds_list[cmd_found].handler(arg_count, args);
327         }
329         return ret;