Adding read-memory command support
[lpctools] / isp_commands.c
1 /*********************************************************************
2  *
3  *   LPC1114 ISP Commands
4  *
5  *
6  * Written by Nathael Pajani <nathael.pajani@nathael.net>
7  * 
8  * This programm is released under the terms of the GNU GPLv3 licence
9  * as can be found on the GNU website : <http://www.gnu.org/licenses/>
10  *
11  *********************************************************************/
14 #include <stdlib.h> /* strtoul */
15 #include <stdio.h>
16 #include <stdint.h>
17 #include <unistd.h>
18 #include <string.h> /* strncmp */
19 #include <ctype.h>
20 #include <errno.h>
22 #include <unistd.h> /* for open, close */
23 #include <fcntl.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
27 #include "isp_utils.h"
29 extern int trace_on;
31 struct isp_command {
32         char* name;
33         int nb_args;
34         int (*handler)(int arg_count, char** args);
35 };
38 /* Max should be 1270 for read memory, a little bit more for writes */
39 #define SERIAL_BUFSIZE  1300
41 #define SYNCHRO_START "?"
42 #define SYNCHRO  "Synchronized\r\n"
43 #define SYNCHRO_OK "OK\r\n"
44 #define DATA_BLOCK_OK "OK\r\n"
45 #define DATA_BLOCK_RESEND "RESEND\r\n"
46 #define SYNCHRO_ECHO_OFF "A 0\r\n"
48 #define ISP_ABORT "\e"
50 #define UNLOCK "U 23130\r\n"
51 #define READ_UID "N\r\n"
52 #define READ_PART_ID "J\r\n"
53 #define READ_BOOT_VERSION "K\r\n"
57 char* error_codes[] = {
58         "CMD_SUCCESS",
59         "INVALID_COMMAND",
60         "SRC_ADDR_ERROR",
61         "DST_ADDR_ERROR",
62         "SRC_ADDR_NOT_MAPPED",
63         "DST_ADDR_NOT_MAPPED",
64         "COUNT_ERROR",
65         "INVALID_SECTOR",
66         "SECTOR_NOT_BLANK",
67         "SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION",
68         "COMPARE_ERROR",
69         "BUSY",
70         "PARAM_ERROR",
71         "ADDR_ERROR",
72         "ADDR_NOT_MAPPED",
73         "CMD_LOCKED",
74         "INVALID_CODE",
75         "INVALID_BAUD_RATE",
76         "INVALID_STOP_BIT",
77         "CODE_READ_PROTECTION_ENABLED",
78 };
80 int isp_ret_code(char* buf, char** endptr)
81 {
82         unsigned int ret = 0;
83         ret = strtoul(buf, endptr, 10);
84         /* FIXME : Find how return code are sent (binary or ASCII) */
85         if (ret > (sizeof(error_codes)/sizeof(char*))) {
86                 printf("Received unknown error code '%u' !\n", ret);
87         } else if ((ret != 0) || trace_on) {
88                 printf("Received error code '%u': %s\n", ret, error_codes[ret]);
89         }
90         return ret;
91 }
94 /* Connect or reconnect to the target.
95  * Return positive or NULL value when connection is OK, or negative value otherwise.
96  */
97 int isp_connect(unsigned int crystal_freq)
98 {
99         char buf[SERIAL_BUFSIZE];
100         char freq[10];
101         
102         snprintf(freq, 8, "%d\r\n", crystal_freq);
104         /* Send synchronize request */
105         if (isp_serial_write(SYNCHRO_START, strlen(SYNCHRO_START)) != strlen(SYNCHRO_START)) {
106                 printf("Unable to send synchronize request.\n");
107                 return -5;
108         }
109         /* Wait for answer */
110         if (isp_serial_read(buf, SERIAL_BUFSIZE, strlen(SYNCHRO)) < 0) {
111                 printf("Error reading synchronize answer.\n");
112                 return -4;
113         }
114         /* Check answer, and acknowledge if OK */
115         if (strncmp(SYNCHRO, buf, strlen(SYNCHRO)) == 0) {
116                 isp_serial_write(SYNCHRO, strlen(SYNCHRO));
117         } else {
118                 printf("Unable to synchronize, no synchro received.\n");
119                 return -3;
120         }
121         /* Empty read buffer (echo is on) */
122         isp_serial_read(buf, strlen(SYNCHRO), strlen(SYNCHRO));
123         /* Read reply (OK) */
124         isp_serial_read(buf, SERIAL_BUFSIZE, strlen(SYNCHRO_OK));
125         if (strncmp(SYNCHRO_OK, buf, strlen(SYNCHRO_OK)) != 0) {
126                 printf("Unable to synchronize, synchro not acknowledged.\n");
127                 return -2;
128         }
130         /* Documentation says we should send crystal frequency .. sending anything is OK */
131         isp_serial_write(freq, strlen(freq));
132         /* Empty read buffer (echo is on) */
133         isp_serial_read(buf, strlen(freq), strlen(freq));
134         /* Read reply (OK) */
135         isp_serial_read(buf, SERIAL_BUFSIZE, strlen(SYNCHRO_OK));
136         if (strncmp(SYNCHRO_OK, buf, strlen(SYNCHRO_OK)) != 0) {
137                 printf("Unable to synchronize, crystal frequency not acknowledged.\n");
138                 return -2;
139         }
141         /* Turn off echo */
142         isp_serial_write(SYNCHRO_ECHO_OFF, strlen(SYNCHRO_ECHO_OFF));
143         /* Empty read buffer (echo still on) */
144         isp_serial_read(buf, strlen(SYNCHRO_ECHO_OFF), strlen(SYNCHRO_ECHO_OFF));
145         /* Read eror code for command */
146         isp_serial_read(buf, SERIAL_BUFSIZE, 3);
148         printf("Device session openned.\n");
150         return 1;
153 int isp_cmd_unlock(int arg_count, char** args)
155         char buf[SERIAL_BUFSIZE];
156         int ret = 0, len = 0;
157         
158         /* Send read-uid request */
159         if (isp_serial_write(UNLOCK, strlen(UNLOCK)) != strlen(UNLOCK)) {
160                 printf("Unable to send unlock request.\n");
161                 return -5;
162         }
163         /* Wait for answer */
164         usleep( 5000 );
165         len = isp_serial_read(buf, SERIAL_BUFSIZE, 1);
166         if (len <= 0) {
167                 printf("Error reading unlock acknowledge.\n");
168                 return -4;
169         }
170         ret = isp_ret_code(buf, NULL);
171         if (ret != 0) {
172                 printf("Unlock error.\n");
173                 return -1;
174         }
175         printf("Device memory protection unlocked.\n");
177         return 0;
180 int isp_cmd_read_uid(int arg_count, char** args)
182         char buf[SERIAL_BUFSIZE];
183         char* tmp = NULL;
184         int i = 0, ret = 0, len = 0;
185         unsigned long int uid[4];
186         
187         /* Send read-uid request */
188         if (isp_serial_write(READ_UID, strlen(READ_UID)) != strlen(READ_UID)) {
189                 printf("Unable to send read-uid request.\n");
190                 return -5;
191         }
192         /* Wait for answer */
193         usleep( 5000 );
194         len = isp_serial_read(buf, SERIAL_BUFSIZE, 50);
195         if (len <= 0) {
196                 printf("Error reading uid.\n");
197                 return -4;
198         }
199         ret = isp_ret_code(buf, &tmp);
200         if (ret != 0) {
201                 printf("This cannot happen ... as long as you trust the user manual.\n");
202                 return -1;
203         }
204         for (i=0; i<4; i++) {
205                 static char* endptr = NULL;
206                 uid[i] = strtoul(tmp, &endptr, 10);
207                 tmp = endptr;
208         }
209         printf("UID: 0x%08lx - 0x%08lx - 0x%08lx - 0x%08lx\n", uid[0], uid[1], uid[2], uid[3]);
211         return 0;
214 int isp_cmd_part_id(int arg_count, char** args)
216         char buf[SERIAL_BUFSIZE];
217         int ret = 0, len = 0;
218         char* tmp = NULL;
219         unsigned long int part_id = 0;
221         /* Send read-part-id request */
222         if (isp_serial_write(READ_PART_ID, strlen(READ_PART_ID)) != strlen(READ_PART_ID)) {
223                 printf("Unable to send read-part-id request.\n");
224                 return -5;
225         }
226         /* Wait for answer */
227         usleep( 5000 );
228         len = isp_serial_read(buf, SERIAL_BUFSIZE, 10);
229         if (len <= 0) {
230                 printf("Error reading part id.\n");
231                 return -4;
232         }
233         ret = isp_ret_code(buf, &tmp);
234         if (ret != 0) {
235                 printf("This cannot happen ... as long as you trust the user manual.\n");
236                 return -1;
237         }
238         /* FIXME : some part IDs are on two 32bits values */
239         part_id = strtoul(tmp, NULL, 10);
240         printf("Part ID is 0x%08lx\n", part_id);
242         return 0;
245 int isp_cmd_boot_version(int arg_count, char** args)
247         char buf[SERIAL_BUFSIZE];
248         int ret = 0, len = 0;
249         char* tmp = NULL;
250         unsigned int ver[2];
252         /* Send read-boot-version request */
253         if (isp_serial_write(READ_BOOT_VERSION, strlen(READ_BOOT_VERSION)) != strlen(READ_BOOT_VERSION)) {
254                 printf("Unable to send read-boot-version request.\n");
255                 return -5;
256         }
257         /* Wait for answer */
258         usleep( 5000 );
259         len = isp_serial_read(buf, SERIAL_BUFSIZE, 5);
260         if (len <= 0) {
261                 printf("Error reading boot version.\n");
262                 return -4;
263         }
264         ret = isp_ret_code(buf, &tmp);
265         if (ret != 0) {
266                 printf("This cannot happen ... as long as you trust the user manual.\n");
267                 return -1;
268         }
269         ver[0] = strtoul(tmp, &tmp, 10);
270         ver[1] = strtoul(tmp, NULL, 10);
271         printf("Boot code version is %u.%u\n", ver[0], ver[1]);
273         return 0;
276 int isp_cmd_read_memory(int arg_count, char** args)
278         /* Serial communication */
279         char buf[SERIAL_BUFSIZE];
280         int ret = 0, len = 0;
281         /* Arguments */
282         unsigned long int addr = 0, count = 0;
283         char* out_file_name = NULL;
284         int out_fd = -1;
285         /* Reply handling */
286         unsigned int lines = 0, blocks = 0;
287         unsigned int total_bytes_received = 0;
288         unsigned int i = 0;
290         /* Check read-memory arguments */
291         if (arg_count != 3) {
292                 printf("read-memory command needs address and count. Both must be multiple of 4.\n");
293                 return -12;
294         }
295         addr = strtoul(args[0], NULL, 0);
296         count = strtoul(args[1], NULL, 0);
297         if (trace_on) {
298                 printf("read-memory command called for 0x%08lx (%lu) bytes at address 0x%08lx.\n",
299                                 count, count, addr);
300         }
301         if ((addr & 0x03) || (count & 0x03)) {
302                 printf("Address and count must be multiple of 4 for read-memory command.\n");
303                 return -11;
304         }
305         out_file_name = args[2];
306         
307         /* Create read-memory request */
308         len = snprintf(buf, SERIAL_BUFSIZE, "R %lu %lu\r\n", addr, count);
309         if (len > SERIAL_BUFSIZE) {
310                 len = SERIAL_BUFSIZE; /* This should not happen if SERIAL_BUFSIZE is 32 or more ... */
311         }
313         /* Send read-memory request */
314         if (isp_serial_write(buf, len) != len) {
315                 printf("Unable to send read-memory request.\n");
316                 return -10;
317         }
318         /* And receive and decode the answer */
319         usleep( 2000 );
320         /* First, check return code. This will also remove the first 3 caracters from the
321      * reply so we get only interresting caracters in the buffer. */
322         len = isp_serial_read(buf, 3, 3);
323         if (len <= 0) {
324                 printf("Error reading memory.\n");
325                 return -9;
326         }
327         ret = isp_ret_code(buf, NULL);
328         if (ret != 0) {
329                 printf("Error when trying to read %lu bytes of memory at address 0x%08lx.\n", count, addr);
330                 return -8;
331         }
333         /* Now, find the number of lines and the length of the reply.
334          * See section 21.4.3 of LPC11xx user's manual (UM10398) */
335 #define LINE_DATA_LENGTH 45
336 #define LINES_PER_BLOCK 20
337 #define MAX_DATA_BLOCK_SIZE (LINES_PER_BLOCK * LINE_DATA_LENGTH) /* 900 */
338 #define MAX_BYTES_PER_LINE (((LINE_DATA_LENGTH / 3) * 4) + 1 + 2) /* 60 + "line length" + \r\n */
339 #define FILE_CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
340         lines = (count / LINE_DATA_LENGTH);
341         if (count % LINE_DATA_LENGTH) {
342                 lines += 1;
343         }
344         blocks = (lines / LINES_PER_BLOCK);
345         if (lines % LINES_PER_BLOCK) {
346                 blocks += 1;
347         }
348         if (trace_on) {
349                 printf("Reading %d block(s) (%d line(s)).\n", blocks, lines);
350         }
351         /* Open file for writing if output is not stdout */
352         if (strncmp(out_file_name, "-", strlen(out_file_name)) == 0) {
353                 out_fd = STDOUT_FILENO;
354         } else {
355                 out_fd = open(out_file_name, (O_WRONLY | O_CREAT | O_TRUNC), FILE_CREATE_MODE);
356                 if (out_fd <= 0) {
357                         perror("Unable to open or create file for writing");
358                         printf("Tried to open \"%s\".\n", out_file_name);
359                         return -7;
360                 }
361         }
362         
363         ret = 0;
364         /* Receive and decode the data */
365         for (i=0; i<blocks; i++) {
366                 char data[SERIAL_BUFSIZE];
367                 unsigned int blocksize = 0, decoded_size = 0;
368                 unsigned int received_checksum = 0, computed_checksum = 0;
369                 static int resend_request_for_block = 0;
370                 unsigned int j = 0;
372                 /* Read the uuencoded data */
373                 if ((count - total_bytes_received) >= MAX_DATA_BLOCK_SIZE) {
374                         blocksize = (MAX_BYTES_PER_LINE * LINES_PER_BLOCK);
375                         if (trace_on) {
376                                 printf("Reading block %d (%d line(s)).\n", i, LINES_PER_BLOCK);
377                         }
378                 } else {
379                         unsigned int remain = count - total_bytes_received;
380                         unsigned int nb_last_lines = (remain / LINE_DATA_LENGTH);
381                         if (remain % LINE_DATA_LENGTH) {
382                                 nb_last_lines += 1;
383                         }
384                         /* 4 bytes transmitted for each 3 data bytes */
385                         blocksize = ((remain / 3) * 4);
386                         if (remain % 3) {
387                                 blocksize += 4;
388                         }
389                         /* Add one byte per line for the starting "length character" and two bytes per
390                          * line for the ending "\r\n" */
391                         blocksize += (3 * nb_last_lines);
392                         if (trace_on) {
393                                 printf("Reading block %d of size %u (%d line(s)).\n", i, blocksize, nb_last_lines);
394                         }
395                 }
396                 len = isp_serial_read(buf, SERIAL_BUFSIZE, blocksize);
397                 if (len <= 0) {
398                         printf("Error reading memory.\n");
399                         ret = -6;
400                         break;
401                 }
402                 usleep( 1000 );
403                 /* Now read the checksum, maybe not yet received */
404                 len += isp_serial_read((buf + len), (SERIAL_BUFSIZE - len), ((len > (int)blocksize) ? 0 : 3));
405                 if (len < 0) { /* Length may be null, as may already have received everything */
406                         printf("Error reading memory (checksum part).\n");
407                         ret = -5;
408                         break;
409                 }
410                 /* Decode. This must be done before sending acknowledge because we must
411                  * compute the checksum */
412                 decoded_size = isp_uu_decode(data, buf, blocksize);
413                 received_checksum = strtoul((buf + blocksize), NULL, 10);
414                 for (j=0; j<decoded_size; j++) {
415                         computed_checksum += (unsigned char)data[j];
416                 }
417                 if (trace_on) {
418                         printf("Decoded Data :\n");
419                         isp_dump((unsigned char*)data, decoded_size);
420                 }
421                 if (computed_checksum == received_checksum) {
422                         resend_request_for_block = 0; /* reset resend request counter */
423                         if (trace_on) {
424                                 printf("Reading of blocks %u OK, contained %u bytes\n", i, decoded_size);
425                         }
426                         /* Acknowledge data */
427                         if (isp_serial_write(DATA_BLOCK_OK, strlen(DATA_BLOCK_OK)) != strlen(DATA_BLOCK_OK)) {
428                                 printf("Unable to send acknowledge.\n");
429                                 ret = -4;
430                                 break;
431                         }
432                         /* Add data length to sum of received data */
433                         total_bytes_received += decoded_size;
434                         /* Write data to file */
435                         ret = write(out_fd, data, decoded_size);
436                         if (ret != (int)decoded_size) {
437                                 perror("File write error");
438                                 printf("Unable to write %u bytes of data to file \"%s\", returned %d !",
439                                                 decoded_size, out_file_name, ret);
440                                 ret = -3;
441                                 break;
442                         }
443                 } else {
444                         resend_request_for_block++;
445                         if (trace_on) {
446                                 printf("Checksum error for block %u (received %u, computed %u) error number: %d.\n",
447                                                 i, received_checksum, computed_checksum, resend_request_for_block);
448                         }
449                         if (resend_request_for_block > 3) {
450                                 printf("Block %d still wrong after 3 attempts, aborting.\n", i);
451                                 ret = -2;
452                                 break;
453                         }
454                         /* Back to previous block */
455                         i--;
456                         /* Ask for resend */
457                         if (isp_serial_write(DATA_BLOCK_RESEND, strlen(DATA_BLOCK_RESEND)) != strlen(DATA_BLOCK_RESEND)) {
458                                 printf("Unable to send resend request.\n");
459                                 ret = -1;
460                                 break;
461                         }
462                 }
463         }
464         /* Close file */
465         if (out_fd != STDOUT_FILENO) {
466                 close(out_fd);
467         }
469         return ret;
473 /* FIXME : Temporary place-holder */
474 int isp_cmd_null(int arg_count, char** args)
476         int i = 0;
477         printf("command not yet handled, called with %d arguments.\n", arg_count);
478         for (i=0; i<arg_count; i++) {
479                 printf("\targ[%d] : \"%s\"\n", i, args[i]);
480         }
481         return 0;
484 static struct isp_command isp_cmds_list[] = {
485         {"unlock", 0, isp_cmd_unlock},
486         {"write-to-ram", 0, isp_cmd_null}, /* isp_cmd_write-to-ram} */
487         {"read-memory", 3, isp_cmd_read_memory},
488         {"prepare-for-write", 0, isp_cmd_null}, /* isp_cmd_prepare-for-write} */
489         {"copy-ram-to-flash", 0, isp_cmd_null}, /* isp_cmd_copy-ram-to-flash} */
490         {"go", 0, isp_cmd_null}, /* isp_cmd_go} */
491         {"erase", 0, isp_cmd_null}, /* isp_cmd_erase} */
492         {"blank-check", 0, isp_cmd_null}, /* isp_cmd_blank-check} */
493         {"read-part-id", 0, isp_cmd_part_id},
494         {"read-boot-version", 0, isp_cmd_boot_version},
495         {"compare", 0, isp_cmd_null}, /* isp_cmd_compare} */
496         {"read-uid", 0, isp_cmd_read_uid},
497         {NULL, 0, NULL}
498 };
500 void isp_warn_trailing_args(int cmd_num, int arg_count, char** args)
502         int i = 0;
503         printf("command \"%s\" needs %d args, got %d.\n",
504                         isp_cmds_list[cmd_num].name,
505                         isp_cmds_list[cmd_num].nb_args, arg_count);
506         for (i=0; i<arg_count; i++) {
507                 printf("\targ[%d] : \"%s\"\n", i, args[i]);
508         }
511 /* Handle one command
512  * Return positive or NULL value when command handling is OK, or negative value otherwise.
513  */
514 int isp_handle_command(char* cmd, int arg_count, char** args)
516         int cmd_found = -1;
517         int ret = 0;
518         int index = 0;
520         if (cmd == NULL) {
521                 printf("isp_handle_command called with no command !\n");
522                 return -1;
523         }
525         while ((cmd_found == -1) && (isp_cmds_list[index].name != NULL)) {
526                 if (strncmp(isp_cmds_list[index].name, cmd, strlen(isp_cmds_list[index].name)) == 0) {
527                         cmd_found = index;
528                         break;
529                 }
530                 index++;
531         }
532         if (arg_count != isp_cmds_list[cmd_found].nb_args) {
533                 isp_warn_trailing_args(cmd_found, arg_count, args);
534         }
535         isp_cmds_list[cmd_found].handler(arg_count, args);
536         
537         if (cmd_found == -1) {
538                 printf("Unknown command \"%s\", use -h or --help for a list.\n", cmd);
539                 return -2;
540         }
542         return ret;