diff options
author | Daniel Thompson <daniel.thompson@linaro.org> | 2017-07-30 08:09:38 +0100 |
---|---|---|
committer | Daniel Thompson <daniel.thompson@linaro.org> | 2017-07-31 17:26:28 +0100 |
commit | d6c0026e2fe557ceb282b89b257003fbf4707104 (patch) | |
tree | 2f60f7fd0ea46148f1f91f71c06aaa389adb24cb | |
parent | 3e0af17f4d561c703a3148556eb76aef6f7fa779 (diff) |
pdl: common: RDA support
-rw-r--r-- | pdl/common/Makefile | 48 | ||||
-rw-r--r-- | pdl/common/packet.c | 301 | ||||
-rw-r--r-- | pdl/common/pdl.c | 28 | ||||
-rw-r--r-- | pdl/common/pdl_channel.c | 39 | ||||
-rw-r--r-- | pdl/common/pdl_channel_usb.c | 85 | ||||
-rw-r--r-- | pdl/common/pdl_debug.c | 48 | ||||
-rw-r--r-- | pdl/common/pdl_engine.c | 141 |
7 files changed, 690 insertions, 0 deletions
diff --git a/pdl/common/Makefile b/pdl/common/Makefile new file mode 100644 index 0000000000..527445b73a --- /dev/null +++ b/pdl/common/Makefile @@ -0,0 +1,48 @@ +# +# (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 + +CPPFLAGS += -I ../include/ +CFLAGS += -I ../include -mno-unaligned-access +LIB := $(obj)libpdl_common.o + +COBJS-y := packet.o pdl_channel.o pdl_channel_usb.o pdl_debug.o pdl_engine.o pdl.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/common/packet.c b/pdl/common/packet.c new file mode 100644 index 0000000000..669d83cc52 --- /dev/null +++ b/pdl/common/packet.c @@ -0,0 +1,301 @@ +//#define DEBUG + +#include <asm/types.h> +#include <common.h> +#include "packet.h" +#include "pdl_channel.h" +#include <linux/string.h> +#include "pdl_debug.h" +#include "cmd_defs.h" +#include <asm/unaligned.h> + +static enum { + PKT_IDLE = 0, + PKT_HEAD, + PKT_DATA, + PKT_ERROR +} pkt_state; + +const static char *pdl_device_rsp[] = { + [ACK] = "device ack", + [PACKET_ERROR] = "packet error", + [INVALID_CMD] = "invalid cmd", + [UNKNOWN_CMD] = "unknown cmd", + [INVALID_ADDR] = "invalid address", + [INVALID_BAUDRATE] = "invalid baudrate", + [INVALID_PARTITION] = "invalid partition", + [INVALID_SIZE] = "invalid size", + [WAIT_TIMEOUT] = "wait timeout", + [VERIFY_ERROR] = "verify error", + [CHECKSUM_ERROR] = "checksum error", + [OPERATION_FAILED] = "operation failed", + [DEVICE_ERROR] = "device error", + [NO_MEMORY] = "no memory", + [DEVICE_INCOMPATIBLE] = "device incompatible", + [HW_TEST_ERROR] = "hardware test error", + [MD5_ERROR] = "md5 error", + [ACK_AGAIN_ERASE] = "ack again erase", + [ACK_AGAIN_FLASH] = "ack again flash", + [MAX_RSP] = "max response", +}; + +static int pkt_error = 0; +static unsigned char cmd_buf[PDL_MAX_PKT_SIZE] __attribute__((__aligned__(32))); +static struct pdl_channel *channel; + +int pdl_init_packet_channel(void) +{ + channel = pdl_get_channel(); + return 0; +} + +/* + * return value: 1, get packet ok; + * 0, not a host packet; + * -1, something error, check pkt_error for true cause; + * -2, don't get any packet + */ +int pdl_get_cmd_packet(struct pdl_packet *pkt) +{ + u32 pkt_size = 0; + u32 hdrsz = sizeof(struct packet_header); + u32 cmd_hdrsz = sizeof(struct command_header); + u32 bufsz = 0, totalsz = 0; + int i, wait = 0; + + if (!pkt) { + pdl_error("packet error\n"); + return -1; + } + + if (!channel->tstc()) + return -2; + + /* looking for HOST_PACKET_TAG 0xAE */ + while (1) { + bufsz = channel->tstc(); + if (!bufsz) + return -2; + channel->read(cmd_buf, bufsz); + if (cmd_buf[0] == HOST_PACKET_TAG) { + /*pdl_info("Found host packet tag, %u bytes\n", bufsz);*/ + break; + } else { + pdl_dbg("Drop a %u bytes packet:", bufsz); + for(i = 0; i < 8; i++) + pdl_dbg(" %02x", cmd_buf[i]); + pdl_dbg("\n"); + } + } + totalsz = bufsz; + + /* is a valid packet header? */ + if (totalsz < hdrsz) { + pdl_error("Invaild packet, it's too small (%u bytes)\n", totalsz); + pkt_state = PKT_IDLE; + return -2; + } + + /* packet header check */ + pkt_state = PKT_HEAD; + memcpy(&pkt_size, &cmd_buf[1], 4); + if (cmd_buf[5] != HOST_PACKET_FLOWID || + pkt_size > PDL_MAX_PKT_SIZE || + pkt_size < sizeof(u32)) { + pdl_error("Invalid packet, flowid 0x%x, pkt_size %u\n", + cmd_buf[5], pkt_size); + pdl_dbg("packet data:"); + for (i = 0; i < 8; i++) + pdl_dbg(" %02x", cmd_buf[i]); + pdl_dbg("\n"); + + pkt_state = PKT_IDLE; + return -2; + } + /*pdl_info("Got command packet, packet size %u bytes\n", pkt_size);*/ + + /* get all data */ + if (pkt_size > totalsz - hdrsz) { + wait = 0; + while (1) { + bufsz = channel->tstc(); + if (bufsz) { + u32 last = pkt_size - (totalsz - hdrsz); + bufsz = (bufsz > last) ? last : bufsz; + channel->read(&cmd_buf[totalsz], bufsz); + totalsz += bufsz; + wait = 0; + if (bufsz == last) + break; + } else { + if (wait++ >= 1000) { + pdl_error("wait packet data timeout, got %u bytes, need %u\n", + totalsz - hdrsz, pkt_size); + pkt_error = PACKET_ERROR; + return -1; + } + udelay(1000); + } + } + } + + pkt_state = PKT_DATA; + memset(pkt, 0, sizeof(struct pdl_packet)); + if (pkt_size < cmd_hdrsz) { + memcpy(pkt, &cmd_buf[hdrsz], pkt_size); + } else { + memcpy(pkt, &cmd_buf[hdrsz], cmd_hdrsz); + pkt->data = &cmd_buf[hdrsz+cmd_hdrsz]; + + /* packet data crc checking, if have 4 bytes crc in tail */ + if (pkt_size >= cmd_hdrsz + pkt->cmd_header.data_size + sizeof(u32)) { + i = hdrsz + cmd_hdrsz + pkt->cmd_header.data_size; + u32 crc = 0, crc_now = 0; + memcpy(&crc, &cmd_buf[i], sizeof(u32)); + crc_now = crc32(0, pkt->data, pkt->cmd_header.data_size); + if (crc != crc_now) { + pdl_error("packet data crc verify failed, expect %#x, got %#x\n", crc, crc_now); + pkt_error = CHECKSUM_ERROR; + return -1; + } + } + } + return 1; +} + + +int pdl_get_connect_packet(struct pdl_packet *pkt, u32 timeout) +{ + char ch = 0; + u8 header_buf[sizeof(struct packet_header)]; + u32 pkt_size = 0; + u32 cnt = timeout; //timeout is in ms; + + while (cnt) { + if(!channel->tstc()) { + mdelay(1); + //pdl_error("try to get\n"); + cnt--; + continue; + } + + ch = channel->getchar(); + if (ch == HOST_PACKET_TAG) { + pkt_state = PKT_HEAD; + break; + } else { + pdl_info("oops, get 0x%x\n", ch); + continue; + } + } + + if (cnt == 0 ) { + pkt_error = WAIT_TIMEOUT; + pdl_error("get connect timeout\n"); + return -1; + } + header_buf[0] = ch; + while (1) { + u8 flowid; + + switch (pkt_state) { + case PKT_IDLE: + break; + case PKT_HEAD: + channel->read(&header_buf[1], 5); + flowid = header_buf[5]; + pkt_size = 0; + if (flowid == HOST_PACKET_FLOWID) { + //ok, this is the header + memcpy(&pkt_size, &header_buf[1], 4); + pkt_size = le32_to_cpu(pkt_size); + pkt_state = PKT_DATA; + pdl_vdbg("header right data_size 0x%x\n", pkt_size); + } else { + pkt_state = PKT_IDLE; + return 0; + } + break; + case PKT_DATA: + //diff the command data size and packet data size,check if it correct; + if (pkt_size > (sizeof(struct command_header) + 1)) { + pkt_error = INVALID_SIZE; + return -1; + } + channel->read(cmd_buf, pkt_size); + memcpy(pkt, cmd_buf, sizeof(struct command_header)); + + pdl_vdbg("pkt cmd type 0x%x data start 0x%x, size %x\n", + pkt->cmd_header.cmd_type, + pkt->cmd_header.data_addr, + pkt->cmd_header.data_size); + return 1; + case PKT_ERROR: + break; + } + } + return 1; +} +/* +static void pdl_put_packet(struct packet *pkt) +{ +} +*/ +void pdl_put_cmd_packet(struct pdl_packet *pkt) +{ + memset(pkt, 0, sizeof (struct pdl_packet)); + pkt_state = PKT_IDLE; + pkt_error = 0; +} + +int pdl_get_packet_error(void) +{ + return pkt_error; +} + +static int pdl_send_data(const u8 *data, u32 size, u8 flowid) +{ + struct packet_header *header = (struct packet_header *)cmd_buf; + int hdr_sz = sizeof(struct packet_header); + + if(size > PDL_MAX_PKT_SIZE - hdr_sz) { + pdl_error("packet size is too large.\n"); + return -1; + } + + memset(cmd_buf, PDL_MAX_PKT_SIZE, 0); + memcpy(&cmd_buf[hdr_sz], data, size); + + header->tag = PACKET_TAG; + header->flowid = flowid; + /* carefully, header->pkt_size may be unaligned */ + put_unaligned(size, &header->pkt_size); /* header->pkt_size = size; */ + + channel->write(cmd_buf, hdr_sz + size); + return 0; +} + +void pdl_send_rsp(int rsp) +{ + /* + * if wait timeout, the client may be exit, we dont send + * this error, for the new client will get a extra error + * response + */ + if (rsp == WAIT_TIMEOUT) + return; + + if (rsp != ACK) { + if (rsp < ACK || rsp > MAX_RSP) + rsp = DEVICE_ERROR; + pdl_info("send rsp '%s'\n", pdl_device_rsp[rsp]); + } + + pdl_send_data((const u8 *)&rsp, sizeof(int), (rsp == ACK) ? FLOWID_ACK : FLOWID_ERROR); + pdl_dbg("send rsp '%s'\n", pdl_device_rsp[rsp]); +} + +int pdl_send_pkt(const u8 *data, u32 size) +{ + return pdl_send_data(data, size, FLOWID_DATA); +} diff --git a/pdl/common/pdl.c b/pdl/common/pdl.c new file mode 100644 index 0000000000..e3bbe27cba --- /dev/null +++ b/pdl/common/pdl.c @@ -0,0 +1,28 @@ +#include <common.h> +#include <asm/arch/hwcfg.h> +#include <asm/arch/rda_sys.h> +#include <pdl.h> + +int pdl_dbg_pdl = 0; +int pdl_vdbg_pdl = 0; +int pdl_dbg_usb_ep0 = 0; +int pdl_dbg_usb_serial = 0; +int pdl_dbg_rw_check = 0; +int pdl_dbg_factory_part = 0; + +/* + check if should start pdl utilities +*/ +int pdl_mode_get(void) +{ + u16 swcfg = rda_swcfg_get(); + + /* + if hwconfig is in force download and swcofing is not + in calibration mode + */ + if (rda_bm_is_download() && !(swcfg & RDA_SW_CFG_BIT_4)) + return 1; + else + return 0; +} diff --git a/pdl/common/pdl_channel.c b/pdl/common/pdl_channel.c new file mode 100644 index 0000000000..313535423c --- /dev/null +++ b/pdl/common/pdl_channel.c @@ -0,0 +1,39 @@ + +#include "pdl_channel.h" +#include "pdl_debug.h" + +static struct pdl_channel *serial_channel = NULL; + +void pdl_channel_register(struct pdl_channel *channel) +{ + if (channel) { + serial_channel = channel; + } else { + pdl_error("channel cannot register\n"); + } + + return; +} + +struct pdl_channel *pdl_get_channel(void) +{ + return serial_channel; +} + +static struct pdl_channel *debug_channel = NULL; + +void pdl_debug_channel_register(struct pdl_channel *channel) +{ + if (channel) { + debug_channel = channel; + } else { + pdl_error("debug channel cannot register\n"); + } + + return; +} + +struct pdl_channel *pdl_get_debug_channel(void) +{ + return debug_channel; +} diff --git a/pdl/common/pdl_channel_usb.c b/pdl/common/pdl_channel_usb.c new file mode 100644 index 0000000000..1bd7d55527 --- /dev/null +++ b/pdl/common/pdl_channel_usb.c @@ -0,0 +1,85 @@ +#include <usb/usbserial.h> +#include "pdl_channel.h" +#include "pdl_debug.h" + +static int usbser_ch0_tstc (void) +{ + return usbser_tstc(USB_ACM_CHAN_0); +} + +static int usbser_ch0_getc (void) +{ + return usbser_getc(USB_ACM_CHAN_0); +} + +static void usbser_ch0_putc (const char c) +{ + usbser_putc(USB_ACM_CHAN_0, c); +} + +static int usbser_ch0_read(unsigned char *_buf, unsigned int len) +{ + return usbser_read(USB_ACM_CHAN_0, _buf, len); +} + +static int usbser_ch0_write(const unsigned char *_buf, unsigned int len) +{ + return usbser_write(USB_ACM_CHAN_0, _buf, len); +} + +static struct pdl_channel usb_channel = { + .getchar = usbser_ch0_getc, + .putchar = usbser_ch0_putc, + .tstc = usbser_ch0_tstc, + .read = usbser_ch0_read, + .write = usbser_ch0_write, + .priv = NULL +}; + +#ifdef CONFIG_USB_ACM_TWO_CHANS + +static int usbser_ch1_tstc (void) +{ + return usbser_tstc(USB_ACM_CHAN_1); +} + +static int usbser_ch1_getc (void) +{ + return usbser_getc(USB_ACM_CHAN_1); +} + +static void usbser_ch1_putc (const char c) +{ + usbser_putc(USB_ACM_CHAN_1, c); +} + +static int usbser_ch1_read(unsigned char *_buf, unsigned int len) +{ + return usbser_read(USB_ACM_CHAN_1, _buf, len); +} + +static int usbser_ch1_write(const unsigned char *_buf, unsigned int len) +{ + return usbser_write(USB_ACM_CHAN_1, _buf, len); +} + +static struct pdl_channel usb_channel2 = { + .getchar = usbser_ch1_getc, + .putchar = usbser_ch1_putc, + .tstc = usbser_ch1_tstc, + .read = usbser_ch1_read, + .write = usbser_ch1_write, + .priv = NULL, +}; + +#endif //CONFIG_USB_ACM_TWO_CHANS + +void pdl_usb_channel_register(void) +{ +#ifdef CONFIG_USB_ACM_TWO_CHANS + pdl_debug_channel_register(&usb_channel2); +#endif + + pdl_channel_register(&usb_channel); +} + diff --git a/pdl/common/pdl_debug.c b/pdl/common/pdl_debug.c new file mode 100644 index 0000000000..07b812bce0 --- /dev/null +++ b/pdl/common/pdl_debug.c @@ -0,0 +1,48 @@ +#include <common.h> +#include <malloc.h> +#include <stdio_dev.h> +#include "pdl.h" +#include "pdl_debug.h" +#include "pdl_channel.h" + +char *pdl_log_buff = NULL; +static void pdl_serial_puts(const char *s) +{ + struct pdl_channel *dbg_ch = pdl_get_debug_channel(); + + /* save print string to log buffer */ + if(pdl_log_buff) { + int a = strlen(pdl_log_buff); + int b = strlen(s); + + if(a + b < PDL_LOG_MAX_SIZE) + strcpy(&pdl_log_buff[a], s); + } + + serial_puts(s); + + if(pdl_dbg_usb_serial && dbg_ch) + dbg_ch->write((const unsigned char *)s, strlen(s)+1); +} + +struct stdio_dev *search_device(int flags, const char *name); +void pdl_init_serial(void) +{ + struct stdio_dev *dev = search_device (DEV_FLAGS_OUTPUT, "serial"); + if(!dev) + return; + dev->puts = pdl_serial_puts; + + if(!pdl_log_buff) { + pdl_log_buff = malloc(PDL_LOG_MAX_SIZE); + pdl_log_buff[0] = '\0'; + } +} +void pdl_release_serial(void) +{ + struct stdio_dev *dev = search_device (DEV_FLAGS_OUTPUT, "serial"); + if(!dev) + return; + dev->puts = serial_puts; +} + diff --git a/pdl/common/pdl_engine.c b/pdl/common/pdl_engine.c new file mode 100644 index 0000000000..2e4c5d3745 --- /dev/null +++ b/pdl/common/pdl_engine.c @@ -0,0 +1,141 @@ +//#define DEBUG +#include <common.h> +#include <asm/types.h> +#include "cmd_defs.h" +#include <asm/arch/rda_sys.h> +#include "packet.h" +#include "pdl_engine.h" +#include "pdl_debug.h" + +static struct pdl_cmd cmd_handler_table[MAX_CMD]; + +#define cmd_is_valid(cmd) ((cmd >= MIN_CMD) && (cmd < MAX_CMD)) + +static const char *pdl_commands[MAX_CMD] = { + [CONNECT] = "connect", + [ERASE_FLASH] = "erase flash", + [ERASE_PARTITION] = "erase partition", + [ERASE_ALL] = "erase all", + [START_DATA] = "start data", + [MID_DATA] = "midst data", + [END_DATA] = "end data", + [EXEC_DATA] = "execute data", + [READ_FLASH] = "read flash", + [READ_PARTITION] = "read partition", + [NORMAL_RESET] = "normal reset", + [READ_CHIPID] = "read chipid", + [SET_BAUDRATE] = "set baudrate", + [FORMAT_FLASH] = "format flash", + [READ_PARTITION_TABLE] = "read partition table", + [READ_IMAGE_ATTR] = "read image attribute", + [GET_VERSION] = "get version", + [SET_FACTMODE] = "set factory mode", + [SET_CALIBMODE] = "set calibration mode", + [SET_PDL_DBG] = "set pdl debuglevel", + [CHECK_PARTITION_TABLE] = "check partition table", + [POWER_OFF] = "power off", + [IMAGE_LIST] = "download image list", + [GET_SECURITY] = "get security capabilities", + [HW_TEST] = "hardware test", + [GET_PDL_LOG] = "get pdl log", + [DOWNLOAD_FINISH] = "download finish", +}; + +int pdl_cmd_register(int cmd_type, pdl_cmd_handler handler) +{ + if (!cmd_is_valid(cmd_type)) + return -1; + + cmd_handler_table[cmd_type].handler = handler; + return 0; +} + +int pdl_handle_connect(u32 timeout) +{ + struct pdl_packet pkt; + int cmd; + int res; + + res = pdl_get_connect_packet(&pkt, timeout); + if (res != 1) { + return -1; + } + + cmd = pkt.cmd_header.cmd_type; + if (cmd == CONNECT) + cmd_handler_table[cmd].handler(&pkt, 0); + else + goto err; + + pdl_put_cmd_packet(&pkt); + return 0; +err: + pdl_put_cmd_packet(&pkt); + return -1; +} + +#define CONNECT_TIMEOUT 180000 /*3 minutes*/ +static void pdl_command_loop(void) +{ + struct pdl_packet pkt; + int res; + u32 cmd; + int error; + int cnt = CONNECT_TIMEOUT; + int check_connect = 1; + + while (1) { + res = pdl_get_cmd_packet(&pkt); + if (res == -2) { + if (!check_connect) { + mdelay(1); + continue; + } else if (--cnt > 0) { + mdelay(1); + continue; + } + + if (cnt <= 0) { + pdl_error("PDL get command timeout...\n"); + break; + } + } + if (check_connect) + check_connect = 0; + + if (res == 0) + continue; + + //if error send response to pc. + if (res == -1) { + error = pdl_get_packet_error(); + pdl_send_rsp(error); + pdl_put_cmd_packet(&pkt); + continue; + } + cmd = pkt.cmd_header.cmd_type; + + if (!cmd_is_valid(cmd)) { + pdl_send_rsp(INVALID_CMD); + pdl_put_cmd_packet(&pkt); + continue; + } + + pdl_dbg("get and exec cmd '%s'\n", pdl_commands[cmd]); + + if (cmd_handler_table[cmd].handler) + res = cmd_handler_table[cmd].handler(&pkt, 0); + pdl_put_cmd_packet(&pkt); + } + + pdl_info("shutdown machine...\n"); + shutdown_system(); +} + +int pdl_handler(void *arg) +{ + for (;;) { + pdl_command_loop(); + } + return 0; +} |