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