From 07260f253b9d198d0f099a0adcdd4eb24e17f102 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Sun, 30 Jul 2017 08:09:38 +0100 Subject: pdl: pdl-2: RDA support --- pdl/pdl-2/Makefile | 49 ++++ pdl/pdl-2/pdl2_command.h | 26 ++ pdl/pdl-2/pdl_command.c | 571 +++++++++++++++++++++++++++++++++++++++ pdl/pdl-2/pdl_emmc.c | 340 +++++++++++++++++++++++ pdl/pdl-2/pdl_emmc.h | 14 + pdl/pdl-2/pdl_main.c | 66 +++++ pdl/pdl-2/pdl_nand.c | 685 +++++++++++++++++++++++++++++++++++++++++++++++ pdl/pdl-2/pdl_nand.h | 31 +++ 8 files changed, 1782 insertions(+) create mode 100644 pdl/pdl-2/Makefile create mode 100644 pdl/pdl-2/pdl2_command.h create mode 100644 pdl/pdl-2/pdl_command.c create mode 100644 pdl/pdl-2/pdl_emmc.c create mode 100644 pdl/pdl-2/pdl_emmc.h create mode 100644 pdl/pdl-2/pdl_main.c create mode 100644 pdl/pdl-2/pdl_nand.c create mode 100644 pdl/pdl-2/pdl_nand.h diff --git a/pdl/pdl-2/Makefile b/pdl/pdl-2/Makefile new file mode 100644 index 0000000000..f342eddcc4 --- /dev/null +++ b/pdl/pdl-2/Makefile @@ -0,0 +1,49 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libpdl_stage.o +CPPFLAGS += -I ../include +CFLAGS += -I ../include -mno-unaligned-access + +COBJS-y += pdl_command.o pdl_main.o pdl_nand.o +COBJS-$(CONFIG_GENERIC_MMC) += pdl_emmc.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/pdl/pdl-2/pdl2_command.h b/pdl/pdl-2/pdl2_command.h new file mode 100644 index 0000000000..a89efe42fa --- /dev/null +++ b/pdl/pdl-2/pdl2_command.h @@ -0,0 +1,26 @@ +#ifndef _PDL2_COMMAND_H_ +#define _PDL2_COMMAND_H_ + +int pdl_command_init(void); +int sys_connect(struct pdl_packet *packet, void *arg); +int data_start(struct pdl_packet *packet, void *arg); +int data_midst(struct pdl_packet *packet, void *arg); +int data_end(struct pdl_packet *packet, void *arg); +int data_exec(struct pdl_packet *packet, void *arg); +int read_partition(struct pdl_packet *packet, void *arg); +int read_partition_table(struct pdl_packet *packet, void *arg); +int format_flash(struct pdl_packet *packet, void *arg); +int read_image_attr(struct pdl_packet *packet, void *arg); +int erase_partition(struct pdl_packet *packet, void *arg); +int get_pdl_version(struct pdl_packet *packet, void *arg); +int reset_machine(struct pdl_packet *packet, void *arg); +int poweroff_machine(struct pdl_packet *packet, void *arg); +int set_pdl_dbg(struct pdl_packet *packet, void *arg); +int check_partition_table(struct pdl_packet *packet, void *arg); +int recv_image_list(struct pdl_packet *packet, void *arg); +int get_pdl_security(struct pdl_packet *packet, void *arg); +int hw_test(struct pdl_packet *packet, void *arg); +int get_pdl_log(struct pdl_packet *packet, void *arg); +int download_finish(struct pdl_packet *packet, void *arg); + +#endif diff --git a/pdl/pdl-2/pdl_command.c b/pdl/pdl-2/pdl_command.c new file mode 100644 index 0000000000..a6c06c17b0 --- /dev/null +++ b/pdl/pdl-2/pdl_command.c @@ -0,0 +1,571 @@ +#include +#include +#include +#include "packet.h" +#include +#include +#include +#include +#include +#include +#include +#include "cmd_defs.h" +#include "pdl_debug.h" +#include "pdl.h" +#include "pdl_command.h" +#include "pdl_nand.h" +#include "pdl_emmc.h" +#include + +#define MAX_PART_NAME 30 +#define MAX_TEST_LIST_SIZE 60 + +#ifdef CONFIG_PDL_FORCE_HW_TEST_FULL +static int hw_tested = 0; +static int hw_test_run(const char *test_list, int fast_mode); +#endif + +uint8_t pdl_extended_status = 0; + +static int media_type = MEDIA_NAND; +int sys_connect(struct pdl_packet *packet, void *arg) +{ + pdl_info("connect with pc\n"); + pdl_send_rsp(ACK); + return 0; +} + +struct exec_file_info { + unsigned long start_address; + unsigned long total_size; + unsigned long recv_size; + unsigned long next_address; +}; +static struct exec_file_info exec_file; +static int frame_count = -1; + +int data_start(struct pdl_packet *packet, void *arg) +{ + unsigned long start_addr = packet->cmd_header.data_addr; + unsigned long file_size = packet->cmd_header.data_size; + int ret; + char part_name[MAX_PART_NAME + 1] = {'\0'}; + + strncpy(part_name, (char *)packet->data, MAX_PART_NAME); + frame_count = -1; + +#ifdef CONFIG_PDL_FORCE_HW_TEST_FULL + if(!hw_tested && + strstr(part_name, "system")) { + ret = hw_test_run("all", 0); + if(ret) { + pdl_send_rsp(ret); + return 0; + } + hw_tested = 1; + } +#endif + + if (strlen(part_name) == 0) { + pdl_info("invalid partition name\n"); + pdl_send_rsp(INVALID_PARTITION); + return -1; + } + + + /* the image is pdl1.bin or pdl2.bin */ + memset(&exec_file, 0, sizeof(exec_file)); + if(strncmp(part_name, "pdl", 3) == 0 || + strncmp(part_name, "PDL", 3) == 0) { + exec_file.start_address = start_addr; + exec_file.total_size = file_size; + exec_file.recv_size = 0; + exec_file.next_address = start_addr; + + pdl_send_rsp(ACK); + return 0; + } + + if (media_type == MEDIA_MMC) + ret = emmc_data_start(part_name, file_size); + else + ret = nand_data_start(part_name, file_size); + if (ret) { + pdl_send_rsp(ret); + return -1; + } + + pdl_send_rsp(ACK); + return 0; +} + +int data_midst(struct pdl_packet *packet, void *arg) +{ + u32 size = packet->cmd_header.data_size; + int ret; + u32 frame_num = packet->cmd_header.data_addr; + + /* pdl1.bin or pdl2.bin */ + if(exec_file.total_size > 0) { + if ((exec_file.recv_size + size) > exec_file.total_size) { + pdl_send_rsp(INVALID_SIZE); + return 0; + } + + memcpy((void *)exec_file.next_address, packet->data, size); + exec_file.next_address += size; + exec_file.recv_size += size; + + pdl_dbg("write to addr %lx, receive size %ld\n", exec_file.next_address, + exec_file.recv_size); + + pdl_send_rsp(ACK); + return 0; + } + + /* check frame number */ + if(frame_count >= 0) { + if(frame_num < frame_count) { /* ignore this frame */ + pdl_send_rsp(ACK); + return 0; + } + if(frame_num > frame_count) { + pdl_send_rsp(PACKET_ERROR); + pdl_error("expect frame %d, not %d\n", frame_count, frame_num); + return -1; + } + } else { + frame_count = frame_num; + } + + pdl_dbg("frame count %u\n", frame_count); + frame_count++; + if (media_type == MEDIA_MMC) + ret = emmc_data_midst(packet->data, size); + else + ret = nand_data_midst(packet->data, size); + if (ret) { + pdl_send_rsp(ret); + return -1; + } + + pdl_send_rsp(ACK); + return 0; +} + +int data_end(struct pdl_packet *packet, void *arg) +{ + int ret; + uint32_t data_size = packet->cmd_header.data_size; + uint32_t data_crc = 0; + + /* pdl1.bin or pdl2.bin */ + if(exec_file.total_size > 0) { + pdl_info("receive pdl1/pdl2 finish\n"); + pdl_send_rsp(ACK); + return 0; + } + + frame_count = -1; + if(data_size == 4) + memcpy((void *)&data_crc, (void *)packet->data, 4); + + if (media_type == MEDIA_MMC) + ret = emmc_data_end(data_crc); + else + ret = nand_data_end(data_crc); + if (ret) { + pdl_send_rsp(ret); + return -1; + } + + pdl_send_rsp(ACK); + return 0; +} + +static unsigned long +do_go_exec (ulong (*entry)(int, char * const []), int argc, char * const argv[]) +{ + return entry (argc, argv); +} + +int data_exec(struct pdl_packet *packet, void *arg) +{ +#ifdef CONFIG_SIGNATURE_CHECK_IMAGE + pdl_info("Verify executable 0x%lx, #%ld\n", + exec_file.start_address,exec_file.recv_size); + if (image_sign_verify((const void *)exec_file.start_address, + exec_file.recv_size)) { + printf("Verify failed, aborting execute.\n"); + pdl_send_rsp(VERIFY_ERROR); + return 0; + } +#endif + pdl_info("Execute download from 0x%lx\n", exec_file.start_address); + do_go_exec((void *)exec_file.start_address, 1, NULL); + return 0; +} + +int read_partition(struct pdl_packet *packet, void *arg) +{ + size_t size = packet->cmd_header.data_size; + char part_name[MAX_PART_NAME + 1] = {'\0'}; + int ret; + size_t actual_len = 0; + unsigned char *data; + + strncpy(part_name, (char *)packet->data, MAX_PART_NAME); + if (strlen(part_name) == 0) { + pdl_info("invalid partition name\n"); + pdl_send_rsp(INVALID_PARTITION); + return -1; + } + + pdl_info("%s from %s\n", __func__, part_name); + + data = malloc(size); + if (!data) { + pdl_info("no memory!\n"); + pdl_send_rsp(NO_MEMORY); + return -1; + } + + if (media_type == MEDIA_MMC) + ret = emmc_read_partition(part_name, data, size, + &actual_len); + else + ret = nand_read_partition(part_name, data, size, + &actual_len); + if (ret) { + pdl_send_rsp(ret); + ret = -1; + goto out; + } + + pdl_send_pkt(data, actual_len); + pdl_dbg("send ok\n"); +out: + free(data); + return ret; +} + +extern int mtdparts_ptbl_check(int *same); +extern int mmc_check_parts(int *same); +int check_partition_table(struct pdl_packet *packet, void *arg) +{ + int ret; + int same = 0; + unsigned char data; + + if (media_type == MEDIA_MMC) + ret = mmc_check_parts(&same); + else + ret = mtdparts_ptbl_check(&same); + + if(ret) { + pdl_send_rsp(DEVICE_ERROR); + return -1; + } + + data = (u8)(same & 0xff); + pdl_send_pkt(&data, 1); + return 0; +} + +int read_partition_table(struct pdl_packet *packet, void *arg) +{ + u32 size; + const char *parts = getenv("mtdparts"); + + pdl_info("%s, parts %s\n", __func__, parts); + + size = strlen(parts); + pdl_send_pkt((const u8*)parts, size); + return 0; +} +extern struct mtd_device *current_mtd_dev; + +int format_flash(struct pdl_packet *packet, void *arg) +{ + int ret; + + pdl_info("format the whole flash part\n"); + + if (media_type == MEDIA_MMC) + ret = emmc_format_flash(); + else + ret = nand_format_flash(); + if (ret) { + pdl_send_rsp(ret); + return -1; + } + +#ifdef CONFIG_PDL_FORCE_HW_TEST_FULL + if(!hw_tested) { + ret = hw_test_run("all", 0); + if(ret) { + pdl_send_rsp(ret); + return 0; + } + hw_tested = 1; + } +#endif + + pdl_send_rsp(ACK); + return 0; +} + +int read_image_attr(struct pdl_packet *packet, void *arg) +{ + const char attrs[] = IMAGE_ATTR; + + pdl_info("fs image attrs: %s\n", attrs); + + pdl_send_pkt((const u8*)attrs, sizeof(attrs)); + return 0; +} + +int erase_partition(struct pdl_packet *packet, void *arg) +{ + int ret; + char part_name[MAX_PART_NAME + 1] = {'\0'}; + + strncpy(part_name, (char *)packet->data, MAX_PART_NAME); + + if (strlen(part_name) == 0) { + pdl_info("invalid partition name\n"); + pdl_send_rsp(INVALID_PARTITION); + return -1; + } + + if (media_type == MEDIA_MMC) + ret = emmc_erase_partition(part_name); + else + ret = nand_erase_partition(part_name); + if (ret) { + pdl_send_rsp(ret); + return -1; + } + + /* if the part is 'userdata', erase 'fat' part also */ + if (strcmp(part_name, "userdata") == 0) { + strcpy(part_name, "fat"); + if (media_type == MEDIA_MMC) + emmc_erase_partition(part_name); + else + nand_erase_partition(part_name); + } + + pdl_send_rsp(ACK); + return 0; +} + +int get_pdl_version(struct pdl_packet *packet, void *arg) +{ + u32 flag = packet->cmd_header.data_addr; + u32 size; +#define MAX_VER_LEN 100 + char str[MAX_VER_LEN+1]; + + memset(str, 0, MAX_VER_LEN + 1); + strncpy(str, version_string, MAX_VER_LEN); + +#ifdef BUILD_DISPLAY_ID + /* return BUILD_DISPLAY_ID instead of pdl version */ + if(flag == 1) + strncpy(str, BUILD_DISPLAY_ID, MAX_VER_LEN); +#endif + + pdl_info("send version string: [%s]\n", str); + + size = strlen(str) + 1; + pdl_send_pkt((const u8*)str, size); + return 0; +} + +int reset_machine(struct pdl_packet *packet, void *arg) +{ + pdl_info("reset machine....\n"); + + u8 reboot_mode = REBOOT_TO_NORMAL_MODE; + if (packet->cmd_header.data_size == 1) { + reboot_mode = packet->data[0]; + } + switch (reboot_mode) { + case REBOOT_TO_NORMAL_MODE: + case REBOOT_TO_DOWNLOAD_MODE: + case REBOOT_TO_FASTBOOT_MODE: + case REBOOT_TO_RECOVERY_MODE: + case REBOOT_TO_CALIB_MODE: + break; + default: + reboot_mode = REBOOT_TO_NORMAL_MODE; + } + + pdl_info("reboot_mode: %d\n", reboot_mode); + pdl_send_rsp(ACK); // cheat and send ack before reset + + rda_reboot(reboot_mode); + + /* should never here */ + return 0; +} + +int poweroff_machine(struct pdl_packet *packet, void *arg) +{ + pdl_info("waiting USB cable plug out...\n"); + while(usb_cable_connected()){ + mdelay(20); + } + pdl_info("machine power off...\n"); + shutdown_system(); + return 0; +} + +int recv_image_list(struct pdl_packet *packet, void *arg) +{ + pdl_info("receive download image list...\n"); + pdl_info("\t%s\n", (char *)packet->data); + prdinfo_init((char *)packet->data); + pdl_send_rsp(ACK); + return 0; +} + +static int hw_test_run(const char *test_list, int fast_mode) +{ + int __attribute__((unused)) test_all = 0; + + if(!test_list) + return HW_TEST_ERROR; + + if(strcmp(test_list, "all") == 0) + test_all = 1; + + pdl_info("start %s hardware test...[%s]\n", fast_mode ? "fast" : "full", test_list); + +#ifdef CONFIG_VPU_STA_TEST + if(test_all || strstr(test_list, "vpu")) { + vpu_sta_test(fast_mode ? 10 : 200); + pdl_info("VPU stable test..pass!\n"); + } +#endif + +#ifdef CONFIG_VPU_MD5_TEST + if(test_all || strstr(test_list, "vpu_md5")) { + int ret = vpu_md5_test(fast_mode ? 10 : 200); + if(ret) + return MD5_ERROR; + pdl_info("VPU MD5 test..pass!\n"); + } +#endif + +#ifdef CONFIG_CPU_TEST + if(test_all || strstr(test_list, "cpu")) { + cpu_pll_test(fast_mode ? 2000 : 4000); + pdl_info("CPU test..pass!\n"); + } +#endif + + pdl_info("end %s hardware test.\n", fast_mode ? "fast" : "full"); + return ACK; +} + +int hw_test(struct pdl_packet *packet, void *arg) +{ + unsigned long param_size = packet->cmd_header.data_size; + char test_list[MAX_TEST_LIST_SIZE + 1] = {'\0'}; + int ret = 0; + + if(param_size > 0) + strncpy(test_list, (char *)packet->data, MAX_TEST_LIST_SIZE); + else + strcpy(test_list, "all"); + + ret = hw_test_run(test_list, 0); + pdl_send_rsp(ret); + return 0; +} + +int get_pdl_log(struct pdl_packet *packet, void *arg) +{ + u32 size; + + pdl_info("get pdl log\n"); + + size = strlen(pdl_log_buff) + 1; + pdl_send_pkt((const u8*)pdl_log_buff, size); + return 0; +} + +int download_finish(struct pdl_packet *packet, void *arg) +{ + u32 ftm_tag = packet->cmd_header.data_addr; + pdl_info("download finish!\n"); + + prdinfo_set_pdl_result(1, ftm_tag); + pdl_send_rsp(ACK); + return 0; +} + +int set_pdl_dbg(struct pdl_packet *packet, void *arg) +{ + u32 dbg_settings = packet->cmd_header.data_addr; + + pdl_info("%s, dbg_settings :%x\n", __func__, dbg_settings); + + if (dbg_settings & PDL_DBG_PDL) { + pdl_info("open pdl debug\n"); + pdl_dbg_pdl = 1; + } + + if (dbg_settings & PDL_DBG_PDL_VERBOSE) { + pdl_info("open pdl verbose debug\n"); + pdl_dbg_pdl = 1; + pdl_vdbg_pdl = 1; + } + + if (dbg_settings & PDL_DBG_USB_EP0) { + pdl_info("open pdl usb ep0 debug\n"); + pdl_dbg_usb_ep0 = 1; + } + + if (dbg_settings & PDL_DBG_RW_CHECK) { + pdl_info("open pdl flash write/read/compare debug\n"); + pdl_dbg_rw_check = 1; + } + + if (dbg_settings & PDL_DBG_USB_SERIAL) { + pdl_info("open pdl usb serial debug\n"); + pdl_dbg_usb_serial = 1; + } + + if (dbg_settings & PDL_DBG_FACTORY_PART) { + pdl_info("open pdl factory part debug\n"); + pdl_dbg_factory_part = 1; + } + if (dbg_settings & PDL_EXTENDED_STATUS) { + pdl_info("Enable extended status\n"); + pdl_extended_status = 1; + } + + pdl_send_rsp(ACK); + return 0; +} + +int pdl_command_init(void) +{ + int ret; + + media_type = rda_media_get(); + if (media_type == MEDIA_MMC) { + ret = pdl_emmc_init(); + } else { + ret = pdl_nand_init(); + } + +#ifdef CONFIG_PDL_FORCE_HW_TEST + ret = hw_test_run("all", 1); +#endif + + return ret; +} diff --git a/pdl/pdl-2/pdl_emmc.c b/pdl/pdl-2/pdl_emmc.c new file mode 100644 index 0000000000..d07be8c07d --- /dev/null +++ b/pdl/pdl-2/pdl_emmc.c @@ -0,0 +1,340 @@ +#include +#include +#include "packet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cmd_defs.h" +#include "pdl_debug.h" +#include "pdl.h" +#include "pdl_command.h" + +#define MAX_PART_NAME 30 +struct dl_file_info { + disk_partition_t *ptn; + uchar *start_addr; + u64 total_size; + u64 recv_size; + char part_name[MAX_PART_NAME]; +}; + +static struct dl_file_info dl_file; + +static uchar *download_buf; +static unsigned long download_max_size; +static block_dev_desc_t *mmc_blkdev = NULL; + +int emmc_data_start(const char *part_name, unsigned long file_size) +{ + disk_partition_t *ptn = 0; + int blksz_shift; + + ptn = partition_find_ptn(part_name); + if(!ptn) { + /* init/update partition table */ + mmc_parts_format(); + /* read again */ + ptn = partition_find_ptn(part_name); + if(!ptn) { + int i; + pdl_info("invalid mtd part:%s ",part_name); + for (i = 0; i < MAX_PART_NAME; i++) + pdl_info(" 0x%x ", part_name[i]); + pdl_info("\n"); + return INVALID_PARTITION; + } + } + + blksz_shift = LOG2(ptn->blksz); + pdl_info("found part '%s' start: 0x%lx length: 0x%lx bytes\n", + ptn->name, ptn->start, (ptn->size << blksz_shift)); + + memset(&dl_file, 0, sizeof(dl_file)); + strncpy(dl_file.part_name, part_name, MAX_PART_NAME); + + if (file_size > (ptn->size << blksz_shift)) { + pdl_info("%s, download file too large, the size is:%ld\n", + __func__, file_size); + pdl_info("but the mmc part %s 's size is %ld\n", + ptn->name, ptn->size << blksz_shift); + return INVALID_SIZE; + } + + dl_file.ptn = ptn; + dl_file.total_size = file_size; + dl_file.start_addr = download_buf; + dl_file.recv_size = 0; + + return 0; +} + +int emmc_data_midst(u8 *data, size_t size) +{ + if (!size) { + pdl_error("oops, send the zero length packet\n"); + return INVALID_SIZE; + } + if (((dl_file.recv_size + size) > dl_file.total_size) || + (dl_file.recv_size + size) > download_max_size) { + pdl_error("transfer size error receive (%lld), file (%lld)\n", + dl_file.recv_size + size, dl_file.total_size); + return INVALID_SIZE; + } + + pdl_dbg("writing 0x%x bytes to offset: 0x%p\n", + size, dl_file.start_addr); + + memcpy(dl_file.start_addr, data, size); + + dl_file.start_addr += size; + dl_file.recv_size += size; + + return 0; +} + +int emmc_data_end(uint32_t crc) +{ + sparse_header_t *sparse_header = (sparse_header_t *)download_buf; + disk_partition_t *ptn = dl_file.ptn; + loff_t write_bytes = dl_file.recv_size; + int ret; + + if (!strcmp(dl_file.part_name, "factorydata")) { + pdl_info("flash to mmc part %s\n", ptn->name); + ret = factory_update_all(download_buf, dl_file.recv_size); + if (ret) { + pdl_error("oops, flash to mmc error %d\n", ret); + return OPERATION_FAILED; + } + return 0; + } + + if (!strcmp(dl_file.part_name, PRDINFO_PART_NAME)) { + pdl_info("flash to mmc part %s\n", ptn->name); + ret = prdinfo_update_all((char *)download_buf, dl_file.recv_size); + if (ret) { + pdl_error("oops, flash to mmc error %d\n", ret); + return OPERATION_FAILED; + } + return 0; + } + + /* verify data crc before write to mmc */ + if(crc) { + uint32_t new_crc = crc32(0, (unsigned char *)download_buf, write_bytes); + if(crc != new_crc) { + pdl_info("CRC verify failed, expect %#x, got %#x.\n", crc, new_crc); + return CHECKSUM_ERROR; + } else { + pdl_info("CRC verify success, %#x.\n", crc); + } + } + + /* Using to judge if ext4 file system */ + if (sparse_header->magic != SPARSE_HEADER_MAGIC) { + pdl_info("flash to mmc part %s\n", ptn->name); + ret = partition_write_bytes(mmc_blkdev, ptn, + &write_bytes, download_buf); + } else { + pdl_info("unsparese flash part %s start %lx, size %ld blks\n", + ptn->name, ptn->start, ptn->size); + ret = partition_unsparse(mmc_blkdev, ptn, download_buf, + ptn->start, ptn->size); + } + if (ret) { + pdl_error("oops, flash to mmc error %d\n", ret); + return OPERATION_FAILED; + } + + prdinfo_set_pdl_image_download_result((char *)ptn->name, 1); + pdl_info("END: total flashed %lld to mmc\n", write_bytes); + return 0; +} + + +int emmc_read_partition(const char *part_name, unsigned char *data, size_t size, + size_t *actual_len) +{ + disk_partition_t *ptn; + loff_t read_bytes = size; + int ret; + + if(!mmc_blkdev) { + mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME); + if (!mmc_blkdev) + return DEVICE_ERROR; + } + + ptn = partition_find_ptn(part_name); + if(!ptn) { + /* init/update partition table */ + mmc_parts_format(); + /* read again */ + ptn = partition_find_ptn(part_name); + if(!ptn) { + pdl_error("partition table doesn't exist"); + return INVALID_PARTITION; + } + } + + pdl_dbg("%s from %s\n", __func__, part_name); + + if (!strcmp(part_name, "factorydata")) { + size = factory_get_all(data); + if (size == 0) { + pdl_error("read from emmc error\n"); + return DEVICE_ERROR; + } + } else { + ret = partition_read_bytes(mmc_blkdev, ptn, &read_bytes, data); + if(ret < 0) { + pdl_error("read from emmc error\n"); + return DEVICE_ERROR; + } + } + if(actual_len) + *actual_len = size; + + pdl_dbg("read actrual %d ok \n", *actual_len); + return 0; +} + +int emmc_write_partition(const char *part_name, unsigned char *data, size_t size) +{ + disk_partition_t *ptn; + loff_t write_bytes = size; + int ret; + + if(!mmc_blkdev) { + mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME); + if (!mmc_blkdev) + return DEVICE_ERROR; + } + + ptn = partition_find_ptn(part_name); + if(!ptn) { + /* init/update partition table */ + mmc_parts_format(); + /* read again */ + ptn = partition_find_ptn(part_name); + if(!ptn) { + pdl_error("partition table doesn't exist"); + return INVALID_PARTITION; + } + } + + pdl_dbg("%s write to %s\n", __func__, part_name); + + if (!strcmp(part_name, "factorydata")) { + return factory_update_all(data, size); + } else { + ret = partition_write_bytes(mmc_blkdev, ptn, &write_bytes, data); + if(ret < 0) { + pdl_error("write to emmc error\n"); + return DEVICE_ERROR; + } + } + + pdl_dbg("write actrual %u ok \n", size); + return 0; +} + +extern struct mtd_device *current_mtd_dev; + +int emmc_format_flash(void) +{ + int ret; + lbaint_t start = 0; + lbaint_t count = 0; + disk_partition_t *ptn_old, *ptn_new; + + if (!mmc_blkdev) { + mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME); + if (!mmc_blkdev) + return DEVICE_ERROR; + } + count = mmc_blkdev->lba; + + ptn_old = partition_find_ptn("factorydata"); + factory_load(); + + pdl_info("refresh partition table\n"); + ret = mmc_parts_format(); + if (ret) { + pdl_error("refresh partition table fail %d\n", ret); + return DEVICE_ERROR; + } + + /* if 'factorydata' part is not changed, don't + erase '0' offset to the end offset of 'factorydata' */ + ptn_new = partition_find_ptn("factorydata"); + if (ptn_old && ptn_new) { + if(ptn_old->start == ptn_new->start && + ptn_old->size == ptn_new->size) + start = ptn_new->start + ptn_new->size; + } + + count -= start; + pdl_info("format the whole mmc, from %ld, %ld blocks\n", start, count); + ret = mmc_blkdev->block_erase(CONFIG_MMC_DEV_NUM, start, count); + if (ret != count) { + pdl_error("mmc format partition fail %d\n", ret); + return DEVICE_ERROR; + } + + /* if 'factorydata' was erased, write back */ + if (!start) + factory_burn(); + + return 0; +} + +int emmc_erase_partition(const char *part_name) +{ + disk_partition_t *ptn; + int ret; + + if(!mmc_blkdev) { + mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME); + if (!mmc_blkdev) + return DEVICE_ERROR; + } + + ptn = partition_find_ptn(part_name); + if(!ptn) { + /* init/update partition table */ + mmc_parts_format(); + /* read again */ + ptn = partition_find_ptn(part_name); + if(!ptn) { + pdl_error("partition table doesn't exist"); + return INVALID_PARTITION; + } + } + + pdl_info("erase the mmc part %s\n", part_name); + ret = partition_erase_blks(mmc_blkdev, ptn, &ptn->size); + if (ret) { + pdl_error("erase parttion fail\n"); + return DEVICE_ERROR; + } + return 0; +} + +int pdl_emmc_init(void) +{ + download_buf = (unsigned char *)SCRATCH_ADDR; + download_max_size = FB_DOWNLOAD_BUF_SIZE; + mmc_blkdev = get_dev_by_name(CONFIG_MMC_DEV_NAME); + if (!mmc_blkdev) + return DEVICE_ERROR; + pdl_dbg("download buffer %p, max size %ld\n", download_buf, download_max_size); + return 0; +} + diff --git a/pdl/pdl-2/pdl_emmc.h b/pdl/pdl-2/pdl_emmc.h new file mode 100644 index 0000000000..de70bdb45c --- /dev/null +++ b/pdl/pdl-2/pdl_emmc.h @@ -0,0 +1,14 @@ +#ifndef __PDL_EMMC_H_ +#define __PDL_EMMC_H_ + +int emmc_data_start(const char *part_name, unsigned long file_size); +int emmc_data_midst(u8 *data, size_t size); +int emmc_data_end(uint32_t crc); +int emmc_read_partition(const char *part_name, unsigned char *data, size_t size, + size_t *actual_len); +int emmc_write_partition(const char *part_name, unsigned char *data, size_t size); +int emmc_format_flash(void); +int emmc_erase_partition(const char *part_name); +int pdl_emmc_init(void); +#endif + diff --git a/pdl/pdl-2/pdl_main.c b/pdl/pdl-2/pdl_main.c new file mode 100644 index 0000000000..4bbe2b072f --- /dev/null +++ b/pdl/pdl-2/pdl_main.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include "pdl_command.h" +#include "packet.h" +#include "cmd_defs.h" +#include "pdl_engine.h" +#include "pdl_debug.h" +#include "pdl_channel.h" +#include + +int pdl_main(void) +{ + int ret = 0; + pdl_info("start pdl2...\n"); + + drv_usbser_init(); + pdl_usb_channel_register(); + pdl_init_packet_channel(); + ret = pdl_command_init(); + if(ret) { + pdl_info("pdl init failed, power down..\n"); + pdl_send_rsp(ret); + shutdown_system(); + } + + /* hack serial_puts(), to save all prints to log buffer */ + pdl_init_serial(); + + pdl_cmd_register(CONNECT, sys_connect); + pdl_cmd_register(START_DATA, data_start); + pdl_cmd_register(MID_DATA, data_midst); + pdl_cmd_register(END_DATA, data_end); + pdl_cmd_register(EXEC_DATA, data_exec); + pdl_cmd_register(READ_PARTITION, read_partition); + pdl_cmd_register(READ_PARTITION_TABLE, read_partition_table); + pdl_cmd_register(FORMAT_FLASH, format_flash); + pdl_cmd_register(READ_IMAGE_ATTR, read_image_attr); + pdl_cmd_register(ERASE_PARTITION, erase_partition); + pdl_cmd_register(NORMAL_RESET, reset_machine); + pdl_cmd_register(POWER_OFF, poweroff_machine); + pdl_cmd_register(GET_VERSION, get_pdl_version); + pdl_cmd_register(SET_PDL_DBG, set_pdl_dbg); + pdl_cmd_register(CHECK_PARTITION_TABLE, check_partition_table); + pdl_cmd_register(IMAGE_LIST, recv_image_list); + pdl_cmd_register(HW_TEST, hw_test); + pdl_cmd_register(GET_PDL_LOG, get_pdl_log); + pdl_cmd_register(DOWNLOAD_FINISH, download_finish); + + pdl_send_rsp(ACK); + pdl_handler(NULL); + return 0; +} + +#if defined(CONFIG_CMD_PDL2) +int do_pdl2(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) +{ + pdl_main(); + return 0; +} + +U_BOOT_CMD(pdl2, 1, 1, do_pdl2, + "android fastboot protocol", "flash image to nand"); +#endif diff --git a/pdl/pdl-2/pdl_nand.c b/pdl/pdl-2/pdl_nand.c new file mode 100644 index 0000000000..db364e9e0f --- /dev/null +++ b/pdl/pdl-2/pdl_nand.c @@ -0,0 +1,685 @@ +#include +#include +#include "packet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cmd_defs.h" +#include "pdl_debug.h" +#include "pdl.h" +#include "pdl_command.h" +#include "pdl_nand.h" +#include +#include + +#define MAX_PART_NAME 30 + +struct dl_file_info { + struct mtd_info *nand; + + struct part_info *part; + unsigned long start_offset; /* partition start offset on flash */ + unsigned long end_offset; /* partition end offset on flash */ + unsigned long write_offset; /* write offset on flash */ + + unsigned char *download_bufp; /* download buffer pointer */ + unsigned long download_size; /* data size in download buffer */ + + unsigned long total_size; /* image file size */ + unsigned long recv_size; /* received data size */ + + char part_name[MAX_PART_NAME]; + char vol_name[MAX_PART_NAME]; +}; + +static struct dl_file_info dl_file; +static unsigned char *download_buf; +static unsigned long download_max_size = (24 * 1024 *1024); + +extern int mtdparts_init_default(void); +extern int mtdparts_init_from_ptbl(void); +extern int mtdparts_save_ptbl(int need_erase); + +static int __erase_partition(nand_info_t *nand, + struct part_info *part) +{ + nand_erase_options_t opts; + int ret; + + pdl_info("%s: erase part '%s', offset 0x%llx size 0x%llx\n", __func__, + part->name, part->offset, part->size); + memset(&opts, 0, sizeof(opts)); + opts.offset = (loff_t) part->offset; + opts.length = (loff_t) part->size; + opts.jffs2 = 0; + opts.quiet = 0; + + pdl_dbg("opts off 0x%08x\n", (uint32_t) opts.offset); + pdl_dbg("opts size 0x%08x\n", (uint32_t) opts.length); + ret = nand_erase_opts(nand, &opts); + if (!ret) { + part->dirty = 0; + } + return ret; +} + +static int pdl_ubi_start_write(void) +{ + int ret = 0; + + /* prepart to write ubi volume image, system/vendor partition */ +#ifdef MTDPARTS_UBI_DEF + pdl_info("init ubi part '%s'\n", dl_file.part_name); + ret = ubi_part_scan(dl_file.part_name); + if(ret) { + pdl_info("ubi init failed.\n"); + return OPERATION_FAILED; + } + + ret = ubi_check_default_vols(MTDPARTS_UBI_DEF); + if(ret) { + pdl_info("ubi volumes check failed.\n"); + return OPERATION_FAILED; + } + + ret = ubi_prepare_write_vol(dl_file.vol_name, dl_file.total_size); +#endif + + return ret; +} + +typedef enum { + PART_RAW = 0, + PART_FACTORYDATA, + PART_PRDINFO, + PART_UBI_VOL, +} PART_TYPE; +static int update_part_type = PART_RAW; +int nand_data_start(const char *part_name, unsigned long file_size) +{ + struct part_info *part; + struct mtd_device *dev; + u8 part_num; + int ret; + + update_part_type = PART_RAW; + memset(&dl_file, 0, sizeof(dl_file)); + strncpy(dl_file.part_name, part_name, MAX_PART_NAME-1); + strncpy(dl_file.vol_name, part_name, MAX_PART_NAME-1); + +#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */ + if(strstr(MTDPARTS_UBI_DEF, part_name)) { + strncpy(dl_file.part_name, MTDPARTS_UBI_PART_NAME, MAX_PART_NAME-1); + strncpy(dl_file.vol_name, part_name, MAX_PART_NAME-1); + pdl_info("'%s' is a ubi volume in '%s' part.\n", + dl_file.vol_name, dl_file.part_name); + update_part_type = PART_UBI_VOL; + } +#endif + + ret = find_dev_and_part(dl_file.part_name, &dev, &part_num, &part); + if (ret) { + int i; + pdl_info("invalid mtd part:%s ", dl_file.part_name); + for (i = 0; i < MAX_PART_NAME; i++) + pdl_info(" 0x%x ", dl_file.part_name[i]); + pdl_info("\n"); + return (INVALID_PARTITION); + } + + pdl_info("found part '%s' offset: 0x%llx length: 0x%llx id: %d is %s\n", + part->name, part->offset, part->size, dev->id->num, + part->dirty ? "dirty" : "clean"); + dl_file.part = part; + dl_file.end_offset = part->offset + part->size; + dl_file.nand = &nand_info[dev->id->num]; + + if (file_size > part->size) { + pdl_info("%s, download file too large, the size is:%ld\n", + __func__, file_size); + pdl_info("but the mtd part %s 's size is %llu\n", + part->name, part->size); + return INVALID_SIZE; + } + + if (!strcmp(part->name, "factorydata")) { + pdl_dbg("update factorydata\n"); + update_part_type = PART_FACTORYDATA; + } else if (!strcmp(part->name, PRDINFO_PART_NAME)) { + pdl_dbg("update prdinfo\n"); + update_part_type = PART_PRDINFO; + } + //erase the mtd part; + if (part->dirty + && (update_part_type == PART_RAW) + ) { + ret = __erase_partition(dl_file.nand, part); + if (ret) { + pdl_info("erase mtd partitions error\n"); + return DEVICE_ERROR; + } + } + /*now we'll write data to the partition, so dirty the part */ + part->dirty = 1; + + dl_file.total_size = file_size; + dl_file.start_offset = part->offset; + dl_file.write_offset = part->offset; + dl_file.download_bufp = download_buf; + dl_file.recv_size = 0; + dl_file.download_size = 0; + + if((update_part_type == PART_UBI_VOL) && pdl_ubi_start_write()) { + pdl_error("can't write ubi volume %s\n", dl_file.vol_name); + return DEVICE_ERROR; + } + + return 0; +} + +#ifdef _TGT_AP_DDR_AUTO_CALI_ENABLE +static void add_ddr_cal_val_to_bootloader(void) +{ + struct nand_chip *chip = dl_file.nand->priv; + struct rda_nand_info *info = chip->priv; + unsigned int cal_addr_flag_offs; + spl_bd_t * spl_board_info = (spl_bd_t *)CONFIG_SPL_BOARD_INFO_ADDR; + u16 ddr_cal[2] = {0}; + if (strcmp(dl_file.part->name, "bootloader") == 0){ + if (spl_board_info->spl_ddr_cal_info.ddr_auto_cal_flag == CONIFG_DDR_CAL_VAL_FLAG) + ddr_cal[0] = spl_board_info->spl_ddr_cal_info.ddr_auto_cal_flag; + ddr_cal[1] = spl_board_info->spl_ddr_cal_info.ddr_auto_cal_val[0]; + /*SPL is 2K in every nand page, hardware limit in 8810 chip*/ + cal_addr_flag_offs = (((spl_board_info->spl_ddr_cal_info.ddr_auto_cal_offs / 2048) + * 2048) * info->spl_adjust_ratio) / 2 + + (spl_board_info->spl_ddr_cal_info.ddr_auto_cal_offs % 2048); + memcpy((void *)(download_buf + cal_addr_flag_offs), + (void *)ddr_cal, 4); + } +} +#endif + +static int pdl_nand_data_write(int data_end) +{ + unsigned int bad_blocks = 0; + unsigned long write_bytes = dl_file.download_size; + int ret = 0; + + pdl_info("flash data to '%s', size %lu bytes.\n", + dl_file.vol_name, write_bytes); + + /* write ubi volume image, system/vendor partition */ + if(update_part_type == PART_UBI_VOL) { +#ifdef MTDPARTS_UBI_DEF + ret = ubi_do_write_vol(dl_file.vol_name, download_buf, dl_file.download_size); + + /* clear download buffer position & size */ + dl_file.download_bufp = download_buf; + dl_file.download_size = 0; + + if(data_end) + ubi_finish_write_vol(dl_file.vol_name); +#endif + + } else if (update_part_type == PART_FACTORYDATA) { + ret = factory_update_all(download_buf, write_bytes); + } else if (update_part_type == PART_PRDINFO) { + ret = prdinfo_update_all((char *)download_buf, write_bytes); + } else { + +#ifdef _TGT_AP_DDR_AUTO_CALI_ENABLE + /* add DDR calibration value to bootloader image, it's a hack, FIXME */ + add_ddr_cal_val_to_bootloader(); +#endif + + ret = nand_write_skip_bad_new(dl_file.nand, dl_file.write_offset, + (size_t *)&write_bytes, + dl_file.end_offset, + download_buf, + 0, &bad_blocks); + + /* clear download buffer position & size */ + dl_file.download_bufp = download_buf; + dl_file.download_size = 0; + /* update write offset */ + dl_file.write_offset += write_bytes; + dl_file.write_offset += bad_blocks * dl_file.nand->erasesize; + + } + if (ret) { + pdl_error("oops, flash to nand error %d\n", ret); + return OPERATION_FAILED; + } + + return 0; +} + +int nand_data_midst(u8 *data, size_t size) +{ + int ret; + extern uint8_t pdl_extended_status; + + if (!size) { + pdl_error("oops, send the zero length packet\n"); + return INVALID_SIZE; + } + if (((dl_file.recv_size + size) > dl_file.total_size)) { + pdl_error("transfer size error receive (%lu), file (%lu)\n", + dl_file.recv_size + size, dl_file.total_size); + return INVALID_SIZE; + } + + /* if buffer is full */ + if((dl_file.download_size + size) > download_max_size) { + if (pdl_extended_status) + pdl_send_rsp(ACK_AGAIN_FLASH); + ret = pdl_nand_data_write(0); + if(ret) { + pdl_info("ERROR: '%s' partition write failed.\n", dl_file.part_name); + return ret; + } + } + + pdl_dbg("writing 0x%x bytes to offset: 0x%08lx\n", + size, (unsigned long)dl_file.download_bufp); + + /* save data to buffer */ + memcpy(dl_file.download_bufp, data, size); + + dl_file.download_bufp += size; + dl_file.download_size += size; + dl_file.recv_size += size; + + return 0; +} + +static int pdl_nand_data_verify(uint32_t crc) +{ + unsigned int bad_blocks = 0; + unsigned long read_bytes = 0; + unsigned long total_size = dl_file.recv_size; + unsigned long read_offset = 0; + uint32_t new_crc = 0; + int ret = 0; + + if(!crc) + return 0; + + /* read back data from nand, for crc verify */ + pdl_info("Read back data from flash to do CRC verify.\n"); + + if(update_part_type == PART_UBI_VOL) { +#ifdef MTDPARTS_UBI_DEF + while(1) { + read_bytes = (total_size > download_max_size) ? download_max_size : total_size; + ret = ubi_read_vol(dl_file.vol_name, read_offset, download_buf, read_bytes); + if(ret) + break; + + new_crc = crc32(new_crc, download_buf, read_bytes); + + if(read_bytes >= total_size) + break; + total_size -= read_bytes; + read_offset += read_bytes; + } +#endif + + } else if (update_part_type == PART_RAW) { + read_offset = dl_file.start_offset; + while(1) { + read_bytes = (total_size > download_max_size) ? download_max_size : total_size; + ret = nand_read_skip_bad_new(dl_file.nand, read_offset, + (size_t *)&read_bytes, download_buf, &bad_blocks); + if(ret) + break; + + new_crc = crc32(new_crc, download_buf, read_bytes); + + if(read_bytes >= total_size) + break; + total_size -= read_bytes; + read_offset += read_bytes + (bad_blocks * dl_file.nand->erasesize); + } + + } else { + return 0; + } + + if (ret) { + pdl_error("oops, read data from nand error %d\n", ret); + return OPERATION_FAILED; + } + + if(crc != new_crc) { + pdl_info("CRC verify failed, expect %#x, got %#x.\n", crc, new_crc); + return CHECKSUM_ERROR; + } else { + pdl_info("CRC verify success, %#x.\n", crc); + } + + return 0; +} + +int nand_data_end(uint32_t crc) +{ + int ret; + + /* force to verify CRC, ignore bootloader/factorydata */ + if ((strcmp(dl_file.part->name, "bootloader") == 0 || + strcmp(dl_file.part->name, "factorydata") == 0) || + strcmp(dl_file.part->name, PRDINFO_PART_NAME) == 0) { + crc = 0; + } else if(!crc) { + pdl_info("ERROR: CRC checking is necessary!"); + pdl_info("ERROR: please use the new version of download tools.\n"); + return CHECKSUM_ERROR; + } + + ret = pdl_nand_data_write(1); + if(ret) { + pdl_info("ERROR: '%s' partition write failed.\n", dl_file.part_name); + return ret; + } + + ret = pdl_nand_data_verify(crc); + if(ret) { + pdl_info("ERROR: '%s' partition data verify failed.\n", dl_file.part_name); + return ret; + } + + pdl_info("END: total flashed %ld to nand\n", dl_file.recv_size); + + dl_file.download_bufp = download_buf; + dl_file.download_size = 0; + update_part_type = PART_RAW; + + /* the mtd partition table is saved in 'bootloader' partition, + * if 'bootloader' is updated, need to re-write the partition table. */ + if(strcmp(dl_file.part->name, "bootloader") == 0) { + mtdparts_save_ptbl(1); + } + + prdinfo_set_pdl_image_download_result(dl_file.part->name, 1); + return 0; +} + + +int nand_read_partition(const char *part_name, u8 *data, size_t size, + size_t *actual_len) +{ + struct part_info *part = NULL; + u8 part_num; + struct mtd_info *nand = NULL; + struct mtd_device *dev; + int ret; + + if(size > download_max_size) { + pdl_info("can't read partition '%s', size is too large\n", part_name); + return OPERATION_FAILED; + } + +#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */ + if(strstr(MTDPARTS_UBI_DEF, part_name)) { + size_t vol_size = ubi_sizeof_vol(part_name); + size = (size > vol_size) ? vol_size: size; + pdl_info("'%s' is a ubi volume in '%s' part, of size %zu.\n", + part_name, MTDPARTS_UBI_PART_NAME, vol_size); + ret = ubi_part_scan(MTDPARTS_UBI_PART_NAME); + if(ret) { + pdl_info("ubi init failed.\n"); + return OPERATION_FAILED; + } + + ret = ubi_read_vol(part_name, 0, data, size); + if(ret) { + pdl_info("read ubi volume error: %s\n", part_name); + return DEVICE_ERROR; + } + if(actual_len) + *actual_len = size; + pdl_info("read volume %s ok, %u bytes\n", part_name, size); + return 0; + } +#endif + + ret = find_dev_and_part(part_name, &dev, &part_num, &part); + if (ret) { + pdl_error("%s: invalid mtd part '%s'\n",__func__, part_name); + return INVALID_PARTITION; + } + + pdl_info("%s: read %#x bytes from '%s'\n", __func__, size, part_name); + nand = &nand_info[dev->id->num]; + pdl_dbg("found part '%s' offset: 0x%llx length: %d/%llu id: %d\n", + part->name, part->offset, size, part->size, dev->id->num); + + size = (size < part->size) ? size : part->size; + + if (!strcmp(part_name, "factorydata")) { + size = factory_get_all(data); + if (size <= 0 ) { + return DEVICE_ERROR; + } + } else { + ret = nand_read_skip_bad(nand, part->offset, &size, + (u_char *)data); + if(ret < 0) { + pdl_error("%s: read from nand error\n", __func__); + return DEVICE_ERROR; + } + } + if(actual_len) + *actual_len = size; + + pdl_dbg("%s: done!", __func__); + return 0; +} + +int nand_write_partition(const char *part_name, unsigned char *data, size_t size) +{ + struct part_info *part = NULL; + u8 part_num; + struct mtd_info *nand = NULL; + struct mtd_device *dev; + int ret; + u32 bad_blocks = 0; + + ret = find_dev_and_part(part_name, &dev, &part_num, &part); + if (ret) { + pdl_error("%s: invalid mtd part '%s'\n",__func__, part_name); + return INVALID_PARTITION; + } + + pdl_info("%s: write %#x bytes to '%s'\n", __func__, size, part_name); + nand = &nand_info[dev->id->num]; + pdl_dbg("found part '%s' offset: 0x%llx length: %d/%llu id: %d\n", + part->name, part->offset, size, part->size, dev->id->num); + + size = (size < part->size) ? size : part->size; + + if (!strcmp(part_name, "factorydata")) { + return factory_update_all(data, size); + } else { + ret = nand_write_skip_bad_new(nand, part->offset, &size, + part->offset + part->size, + (u_char *)data, 0, &bad_blocks); + part->dirty = 1; + if(ret < 0) { + pdl_error("%s: write to nand error.\n", __func__); + return DEVICE_ERROR; + } + } + + pdl_dbg("%s: done!\n", __func__); + return 0; +} + +extern struct mtd_device *current_mtd_dev; + +extern int mtdparts_ptbl_check_factorydata(int *same); +int nand_format_flash(void) +{ + nand_erase_options_t opts; + nand_info_t *nand; + u8 nand_num= 0; + int same = 0; + + /* re-init partition table by default */ + if(mtdparts_init_default()) { + pdl_info("mtdparts init failed, abandon flash format..\n"); + return DEVICE_ERROR; + } + + pdl_info("format the whole flash part\n"); + if (!current_mtd_dev) { + pdl_info("%s mtd device info error\n", __func__); + return DEVICE_ERROR; + } + /* + *erase all flash parts + */ + nand_num = current_mtd_dev->id->num; + nand = &nand_info[nand_num]; + if (!nand) { + pdl_info("%s mtd device info error\n", __func__); + return DEVICE_ERROR; + } + memset(&opts, 0, sizeof(opts)); + opts.offset = 0; + opts.length = (loff_t) nand->size; + opts.jffs2 = 0; + opts.quiet = 0; + opts.scrub = 1; + + /* if partition bootloader & factorydata is not changed, don't erase them */ + mtdparts_ptbl_check_factorydata(&same); + if(same) { + struct part_info *part; + struct mtd_device *dev; + u8 part_num; + int ret = find_dev_and_part("factorydata", &dev, &part_num, &part); + if (!ret) { + opts.offset = part->offset + part->size; + opts.length -= opts.offset; + } + } + factory_load(); + + pdl_dbg("opts off 0x%08x\n", (uint32_t) opts.offset); + pdl_dbg("opts size 0x%08x\n", (uint32_t) opts.length); + pdl_dbg("nand write size 0x%08x\n", nand->writesize); + pdl_info("erase 0x%llx bytes to '%s' offset: 0x%llx\n", + opts.length, nand->name, opts.offset); + nand_erase_opts(nand, &opts); + mtdparts_clear_all_dirty(current_mtd_dev); + + /* enable dirty for bootloader & factorydata part, if need */ + if(opts.offset > 0) { + struct part_info *part; + struct mtd_device *dev; + u8 part_num; + int ret = find_dev_and_part("bootloader", &dev, &part_num, &part); + if (!ret) { + part->dirty = 1; + } + ret = find_dev_and_part("factorydata", &dev, &part_num, &part); + if (!ret) { + part->dirty = 1; + } + } + + /* if 'factorydata' was erased, write back */ + if (opts.offset <= 0) + factory_burn(); + + return 0; +} + +int nand_erase_partition(const char *part_name) +{ + struct part_info *part; + struct mtd_device *dev; + u8 part_num; + int ret; + struct mtd_info *nand; + +#ifdef MTDPARTS_UBI_DEF /* all ubi volumes are in one ubi part */ + if(strstr(MTDPARTS_UBI_DEF, part_name)) { + pdl_info("'%s' is a ubi volume in '%s' part, erase volume.\n", + part_name, MTDPARTS_UBI_PART_NAME); + ret = ubi_part_scan(MTDPARTS_UBI_PART_NAME); + if(ret) { + pdl_info("ubi init failed.\n"); + return OPERATION_FAILED; + } + + ret = ubi_erase_vol((char *)part_name); + if(ret) { + pdl_info("erase ubi volume error: %s\n", part_name); + return DEVICE_ERROR; + } + pdl_info("erase ubi volume ok: %s\n", part_name); + return 0; + } +#endif + + ret = find_dev_and_part(part_name, &dev, &part_num, &part); + if (ret) { + pdl_error("%s: invalid mtd part '%s'\n", __func__, part_name); + return INVALID_PARTITION; + } + + pdl_dbg("%s: erase part '%s'\n", __func__, part_name); + pdl_dbg("found part '%s' offset: 0x%llx length: 0x%llx id: %d\n", + part->name, part->offset, part->size, dev->id->num); + nand = &nand_info[dev->id->num]; + + //erase the mtd part; + ret = __erase_partition(nand, part); + if (ret) { + pdl_error("%s: erase part error\n", __func__); + return DEVICE_ERROR; + } + + pdl_dbg("%s: done!", __func__); + return 0; +} + +int pdl_nand_init(void) +{ + int ret; + download_buf = (unsigned char *)SCRATCH_ADDR; + download_max_size = 24*1024*1024;//FB_DOWNLOAD_BUF_SIZE; + pdl_dbg("download buffer %p, max size %ld\n", download_buf, download_max_size); + + ret = mtdparts_init_from_ptbl(); + /* if fail to load partition table, try default */ + if(ret) { + ret = mtdparts_init_default(); + } + if(ret) { + return DEVICE_ERROR; + } + + /* check page size */ +#ifdef NAND_PAGE_SIZE + if(current_mtd_dev) { + nand_info_t *nand = &nand_info[current_mtd_dev->id->num]; + if(nand->writesize != NAND_PAGE_SIZE) { + pdl_info("nand page size is %#x, expect %#x, not compatible.\n", + nand->writesize, NAND_PAGE_SIZE); + return DEVICE_INCOMPATIBLE; + } + } +#endif + + return 0; +} + diff --git a/pdl/pdl-2/pdl_nand.h b/pdl/pdl-2/pdl_nand.h new file mode 100644 index 0000000000..21df6c7ae3 --- /dev/null +++ b/pdl/pdl-2/pdl_nand.h @@ -0,0 +1,31 @@ +#ifndef __PDL_NAND_H_ +#define __PDL_NAND_H_ + +int nand_data_start(const char *part_name, unsigned long file_size); +int nand_data_midst(u8 *data, size_t size); +int nand_data_end(uint32_t crc); +int nand_read_partition(const char *part_name, unsigned char *data, + size_t size, size_t *actual_len); +int nand_write_partition(const char *part_name, unsigned char *data, + size_t size); +int nand_format_flash(void); +int nand_erase_partition(const char *part_name); + +int pdl_nand_init(void); + +#ifdef MTDPARTS_UBI_DEF +int ubi_part_scan(const char *part_name); +int ubi_check_default_vols(const char *ubi_default_str); +int ubi_erase_vol(const char *vol_name); +int ubi_read_vol(const char *vol_name, loff_t offp, void *buf, size_t size); +int ubi_update_vol(const char *vol_name, void *buf, size_t size); +size_t ubi_sizeof_vol(const char *vol_name); + +/* write a volume by multi parts. */ +int ubi_prepare_write_vol(const char *volume, size_t size); +int ubi_do_write_vol(const char *volume, void *buf, size_t size); +int ubi_finish_write_vol(const char *volume); +#endif + +#endif + -- cgit v1.2.3