Adding read-memory command support
authorNathael Pajani <nathael.pajani@ed3l.fr>
Sun, 15 Apr 2012 21:36:17 +0000 (23:36 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Sun, 15 Apr 2012 21:36:17 +0000 (23:36 +0200)
isp_commands.c
isp_main.c

index 1892ace..f269da9 100644 (file)
 #include <ctype.h>
 #include <errno.h>
 
+#include <unistd.h> /* for open, close */
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
 #include "isp_utils.h"
 
 extern int trace_on;
@@ -30,11 +35,14 @@ struct isp_command {
 };
 
 
-#define SERIAL_BUFSIZE  128
+/* Max should be 1270 for read memory, a little bit more for writes */
+#define SERIAL_BUFSIZE  1300
 
 #define SYNCHRO_START "?"
 #define SYNCHRO  "Synchronized\r\n"
 #define SYNCHRO_OK "OK\r\n"
+#define DATA_BLOCK_OK "OK\r\n"
+#define DATA_BLOCK_RESEND "RESEND\r\n"
 #define SYNCHRO_ECHO_OFF "A 0\r\n"
 
 #define ISP_ABORT "\e"
@@ -45,6 +53,7 @@ struct isp_command {
 #define READ_BOOT_VERSION "K\r\n"
 
 
+
 char* error_codes[] = {
        "CMD_SUCCESS",
        "INVALID_COMMAND",
@@ -264,6 +273,202 @@ int isp_cmd_boot_version(int arg_count, char** args)
        return 0;
 }
 
+int isp_cmd_read_memory(int arg_count, char** args)
+{
+       /* Serial communication */
+       char buf[SERIAL_BUFSIZE];
+       int ret = 0, len = 0;
+       /* Arguments */
+       unsigned long int addr = 0, count = 0;
+       char* out_file_name = NULL;
+       int out_fd = -1;
+       /* Reply handling */
+       unsigned int lines = 0, blocks = 0;
+       unsigned int total_bytes_received = 0;
+       unsigned int i = 0;
+
+       /* Check read-memory arguments */
+       if (arg_count != 3) {
+               printf("read-memory command needs address and count. Both must be multiple of 4.\n");
+               return -12;
+       }
+       addr = strtoul(args[0], NULL, 0);
+       count = strtoul(args[1], NULL, 0);
+       if (trace_on) {
+               printf("read-memory command called for 0x%08lx (%lu) bytes at address 0x%08lx.\n",
+                               count, count, addr);
+       }
+       if ((addr & 0x03) || (count & 0x03)) {
+               printf("Address and count must be multiple of 4 for read-memory command.\n");
+               return -11;
+       }
+       out_file_name = args[2];
+       
+       /* Create read-memory request */
+       len = snprintf(buf, SERIAL_BUFSIZE, "R %lu %lu\r\n", addr, count);
+       if (len > SERIAL_BUFSIZE) {
+               len = SERIAL_BUFSIZE; /* This should not happen if SERIAL_BUFSIZE is 32 or more ... */
+       }
+
+       /* Send read-memory request */
+       if (isp_serial_write(buf, len) != len) {
+               printf("Unable to send read-memory request.\n");
+               return -10;
+       }
+       /* And receive and decode the answer */
+       usleep( 2000 );
+       /* First, check return code. This will also remove the first 3 caracters from the
+     * reply so we get only interresting caracters in the buffer. */
+       len = isp_serial_read(buf, 3, 3);
+       if (len <= 0) {
+               printf("Error reading memory.\n");
+               return -9;
+       }
+       ret = isp_ret_code(buf, NULL);
+       if (ret != 0) {
+               printf("Error when trying to read %lu bytes of memory at address 0x%08lx.\n", count, addr);
+               return -8;
+       }
+
+       /* Now, find the number of lines and the length of the reply.
+        * See section 21.4.3 of LPC11xx user's manual (UM10398) */
+#define LINE_DATA_LENGTH 45
+#define LINES_PER_BLOCK 20
+#define MAX_DATA_BLOCK_SIZE (LINES_PER_BLOCK * LINE_DATA_LENGTH) /* 900 */
+#define MAX_BYTES_PER_LINE (((LINE_DATA_LENGTH / 3) * 4) + 1 + 2) /* 60 + "line length" + \r\n */
+#define FILE_CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
+       lines = (count / LINE_DATA_LENGTH);
+       if (count % LINE_DATA_LENGTH) {
+               lines += 1;
+       }
+       blocks = (lines / LINES_PER_BLOCK);
+       if (lines % LINES_PER_BLOCK) {
+               blocks += 1;
+       }
+       if (trace_on) {
+               printf("Reading %d block(s) (%d line(s)).\n", blocks, lines);
+       }
+       /* Open file for writing if output is not stdout */
+       if (strncmp(out_file_name, "-", strlen(out_file_name)) == 0) {
+               out_fd = STDOUT_FILENO;
+       } else {
+               out_fd = open(out_file_name, (O_WRONLY | O_CREAT | O_TRUNC), FILE_CREATE_MODE);
+               if (out_fd <= 0) {
+                       perror("Unable to open or create file for writing");
+                       printf("Tried to open \"%s\".\n", out_file_name);
+                       return -7;
+               }
+       }
+       
+       ret = 0;
+       /* Receive and decode the data */
+       for (i=0; i<blocks; i++) {
+               char data[SERIAL_BUFSIZE];
+               unsigned int blocksize = 0, decoded_size = 0;
+               unsigned int received_checksum = 0, computed_checksum = 0;
+               static int resend_request_for_block = 0;
+               unsigned int j = 0;
+
+               /* Read the uuencoded data */
+               if ((count - total_bytes_received) >= MAX_DATA_BLOCK_SIZE) {
+                       blocksize = (MAX_BYTES_PER_LINE * LINES_PER_BLOCK);
+                       if (trace_on) {
+                               printf("Reading block %d (%d line(s)).\n", i, LINES_PER_BLOCK);
+                       }
+               } else {
+                       unsigned int remain = count - total_bytes_received;
+                       unsigned int nb_last_lines = (remain / LINE_DATA_LENGTH);
+                       if (remain % LINE_DATA_LENGTH) {
+                               nb_last_lines += 1;
+                       }
+                       /* 4 bytes transmitted for each 3 data bytes */
+                       blocksize = ((remain / 3) * 4);
+                       if (remain % 3) {
+                               blocksize += 4;
+                       }
+                       /* Add one byte per line for the starting "length character" and two bytes per
+                        * line for the ending "\r\n" */
+                       blocksize += (3 * nb_last_lines);
+                       if (trace_on) {
+                               printf("Reading block %d of size %u (%d line(s)).\n", i, blocksize, nb_last_lines);
+                       }
+               }
+               len = isp_serial_read(buf, SERIAL_BUFSIZE, blocksize);
+               if (len <= 0) {
+                       printf("Error reading memory.\n");
+                       ret = -6;
+                       break;
+               }
+               usleep( 1000 );
+               /* Now read the checksum, maybe not yet received */
+               len += isp_serial_read((buf + len), (SERIAL_BUFSIZE - len), ((len > (int)blocksize) ? 0 : 3));
+               if (len < 0) { /* Length may be null, as may already have received everything */
+                       printf("Error reading memory (checksum part).\n");
+                       ret = -5;
+                       break;
+               }
+               /* Decode. This must be done before sending acknowledge because we must
+                * compute the checksum */
+               decoded_size = isp_uu_decode(data, buf, blocksize);
+               received_checksum = strtoul((buf + blocksize), NULL, 10);
+               for (j=0; j<decoded_size; j++) {
+                       computed_checksum += (unsigned char)data[j];
+               }
+               if (trace_on) {
+                       printf("Decoded Data :\n");
+                       isp_dump((unsigned char*)data, decoded_size);
+               }
+               if (computed_checksum == received_checksum) {
+                       resend_request_for_block = 0; /* reset resend request counter */
+                       if (trace_on) {
+                               printf("Reading of blocks %u OK, contained %u bytes\n", i, decoded_size);
+                       }
+                       /* Acknowledge data */
+                       if (isp_serial_write(DATA_BLOCK_OK, strlen(DATA_BLOCK_OK)) != strlen(DATA_BLOCK_OK)) {
+                               printf("Unable to send acknowledge.\n");
+                               ret = -4;
+                               break;
+                       }
+                       /* Add data length to sum of received data */
+                       total_bytes_received += decoded_size;
+                       /* Write data to file */
+                       ret = write(out_fd, data, decoded_size);
+                       if (ret != (int)decoded_size) {
+                               perror("File write error");
+                               printf("Unable to write %u bytes of data to file \"%s\", returned %d !",
+                                               decoded_size, out_file_name, ret);
+                               ret = -3;
+                               break;
+                       }
+               } else {
+                       resend_request_for_block++;
+                       if (trace_on) {
+                               printf("Checksum error for block %u (received %u, computed %u) error number: %d.\n",
+                                               i, received_checksum, computed_checksum, resend_request_for_block);
+                       }
+                       if (resend_request_for_block > 3) {
+                               printf("Block %d still wrong after 3 attempts, aborting.\n", i);
+                               ret = -2;
+                               break;
+                       }
+                       /* Back to previous block */
+                       i--;
+                       /* Ask for resend */
+                       if (isp_serial_write(DATA_BLOCK_RESEND, strlen(DATA_BLOCK_RESEND)) != strlen(DATA_BLOCK_RESEND)) {
+                               printf("Unable to send resend request.\n");
+                               ret = -1;
+                               break;
+                       }
+               }
+       }
+       /* Close file */
+       if (out_fd != STDOUT_FILENO) {
+               close(out_fd);
+       }
+
+       return ret;
+}
+
 
 /* FIXME : Temporary place-holder */
 int isp_cmd_null(int arg_count, char** args)
@@ -279,7 +484,7 @@ int isp_cmd_null(int arg_count, char** args)
 static struct isp_command isp_cmds_list[] = {
        {"unlock", 0, isp_cmd_unlock},
        {"write-to-ram", 0, isp_cmd_null}, /* isp_cmd_write-to-ram} */
-       {"read-memory", 0, isp_cmd_null}, /* isp_cmd_read-memory} */
+       {"read-memory", 3, isp_cmd_read_memory},
        {"prepare-for-write", 0, isp_cmd_null}, /* isp_cmd_prepare-for-write} */
        {"copy-ram-to-flash", 0, isp_cmd_null}, /* isp_cmd_copy-ram-to-flash} */
        {"go", 0, isp_cmd_null}, /* isp_cmd_go} */
index 0b9ae27..f9e4dd2 100644 (file)
@@ -50,7 +50,7 @@ void help(char *prog_name)
                "  <command> is one of:\n" \
                "  \t unlock \n" \
                "  \t write-to-ram \n" \
-               "  \t read-memory \n" \
+               "  \t read-memory address count file : read 'count' byte(s) from 'address', store then in 'file'\n" \
                "  \t prepare-for-write \n" \
                "  \t copy-ram-to-flash \n" \
                "  \t go \n" \