| #include <stdio.h> | 
 | #include <inttypes.h> | 
 | #include <string.h> | 
 |  | 
 | #include "aboot-tiny.h" | 
 | #include "jacana_firmware.h" | 
 | #include "jacana_pvt.h" | 
 |  | 
 | /*---------------------------------------------------------------------------*/ | 
 | static int | 
 | firmware_read_next_to_cache(firmware_handle_t *firmware) | 
 | { | 
 |   size_t size = firmware->end_ptr - firmware->read_ptr; | 
 |   memmove(firmware->read_ptr - firmware->read_sz, firmware->read_ptr, size); | 
 |   firmware->read_ptr -= firmware->read_sz; | 
 |   firmware->end_ptr -= firmware->read_sz; | 
 |   size_t remainder = firmware->end - firmware->start; | 
 |   if(remainder > 0) { | 
 |     size_t size = remainder > firmware->read_sz ? firmware->read_sz : remainder; | 
 |     int len = jacana_firmware_raw_read(firmware->priv, firmware->start, | 
 |                                        (uint8_t *)firmware->middle_ptr, size); | 
 |     if(len < 0) { | 
 |       return -1; | 
 |     } | 
 |     firmware->start += len; | 
 |     firmware->end_ptr += len; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 | /*---------------------------------------------------------------------------*/ | 
 | int | 
 | jacana_firmware_open(firmware_handle_t *firmware, void *priv) | 
 | { | 
 |   firmware->priv = priv; | 
 |   firmware->start = 0; | 
 |   firmware->end = jacana_firmware_raw_get_total_size(priv); | 
 |  | 
 |   firmware->data = aboot_tiny_mem_alloc(MAX_READ_CACHE_SZ * 2); | 
 |   if(!firmware->data) { | 
 |     aboot_tiny_log_printf("Failed: can not alloc data buf\n"); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   firmware->read_sz = MAX_READ_CACHE_SZ; | 
 |   firmware->middle_ptr = (char *)firmware->data + firmware->read_sz; | 
 |   firmware->end_ptr = firmware->middle_ptr + firmware->read_sz; | 
 |   firmware->read_ptr = firmware->end_ptr; | 
 |   if(firmware_read_next_to_cache(firmware) < 0) { | 
 |     goto failed; | 
 |   } | 
 |  | 
 |   char cmd_line[ABOOT_COMMAND_SZ]; | 
 |   char magic[9]; | 
 |   uint32_t size; | 
 |   int num; | 
 |  | 
 |   if(jacana_firmware_read_line(firmware, cmd_line) <= 0) { | 
 |     aboot_tiny_log_printf("Failed: can not read a cmd line\n"); | 
 |     goto failed; | 
 |   } | 
 |   num = sscanf(cmd_line, "%08s%" SCNx32, magic, &size); | 
 |   if(num != 2 || size > firmware->end) { | 
 |     aboot_tiny_log_printf("Failed: cmd line format error\n"); | 
 |     goto failed; | 
 |   } | 
 |   firmware->end = (size_t)size; | 
 |   if(strcmp(magic, "!JACANA!")) { | 
 |     aboot_tiny_log_printf("Magic error: not a valid firmware\n"); | 
 |     goto failed; | 
 |   } | 
 |  | 
 |   return 0; | 
 |  | 
 | failed: | 
 |   aboot_tiny_mem_free(firmware->data); | 
 |   firmware->data = NULL; | 
 |   return -1; | 
 | } | 
 | /*---------------------------------------------------------------------------*/ | 
 | int | 
 | jacana_firmware_read_line(firmware_handle_t *firmware, char *line) | 
 | { | 
 |   while(1) { | 
 |     if(firmware->read_ptr < firmware->middle_ptr) { | 
 |       const char *p = (const char *)strchr(firmware->read_ptr, '\n'); | 
 |       if(!p || p >= firmware->end_ptr) { | 
 |         return -1; | 
 |       } | 
 |       int len = p - firmware->read_ptr + 1; | 
 |       memcpy(line, firmware->read_ptr, len); | 
 |       line[len] = '\0'; | 
 |       firmware->read_ptr += len; | 
 |       if(firmware->read_ptr == firmware->end_ptr) { | 
 |         /* firmware download finished, prepare for continue with pvt */ | 
 |         void *priv = jacana_pvt_raw_open(); | 
 |         if(!priv) { | 
 |           return -1; | 
 |         } | 
 |         jacana_firmware_raw_close(firmware->priv); | 
 |         jacana_firmware_close(firmware); | 
 |         if(jacana_pvt_open(firmware, priv) < 0) { | 
 |           return -1; | 
 |         } | 
 |         aboot_tiny_firmware_read_line = jacana_pvt_read_line; | 
 |         aboot_tiny_firmware_read_data = jacana_pvt_read_data; | 
 |       } | 
 |       if(line[0] == '\n') { | 
 |         continue; | 
 |       } else { | 
 |         line[len - 1] = '\0'; /* replace '\n' to '\0' */ | 
 |         return len - 1; | 
 |       } | 
 |     } else { | 
 |       /* read_ptr >= middle_ptr */ | 
 |       if(firmware_read_next_to_cache(firmware) < 0) { | 
 |         return -1; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 | /*---------------------------------------------------------------------------*/ | 
 | int | 
 | jacana_firmware_read_data(firmware_handle_t *firmware, uint8_t *data, size_t size) | 
 | { | 
 |   while(1) { | 
 |     if(firmware->read_ptr == firmware->end_ptr) { | 
 |       return 0; | 
 |     } | 
 |  | 
 |     if(firmware->read_ptr < firmware->middle_ptr) { | 
 |       size_t remainder = firmware->middle_ptr - firmware->read_ptr; | 
 |       if(remainder < size) { | 
 |         size = remainder; | 
 |       } | 
 |       memcpy(data, firmware->read_ptr, size); | 
 |       firmware->read_ptr += size; | 
 |       return size; | 
 |     } else { | 
 |       /* read_ptr >= middle_ptr */ | 
 |       if(firmware_read_next_to_cache(firmware) < 0) { | 
 |         return -1; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 | /*---------------------------------------------------------------------------*/ | 
 | void | 
 | jacana_firmware_close(firmware_handle_t *firmware) | 
 | { | 
 |   firmware->priv = NULL; | 
 |   firmware->start = 0; | 
 |   firmware->end = 0; | 
 |   if(firmware->data) { | 
 |     aboot_tiny_mem_free(firmware->data); | 
 |     firmware->data = NULL; | 
 |   } | 
 |   firmware->read_sz = 0; | 
 |   firmware->read_ptr = NULL; | 
 |   firmware->middle_ptr = NULL; | 
 |   firmware->end_ptr = NULL; | 
 | } | 
 | /*---------------------------------------------------------------------------*/ |