Quelltext /~heha/hsn/bl/stm32flash.zip/src/stm32.cpp

#include "stm32.h"
#include "port.h"

#define STM32_ACK	0x79
#define STM32_NACK	0x1F
#define STM32_BUSY	0x76

#define STM32_CMD_INIT	0x7F
#define STM32_CMD_GET	0x00	/* get the version and command supported */
#define STM32_CMD_GVR	0x01	/* get version and read protection status */
#define STM32_CMD_GID	0x02	/* get ID */
#define STM32_CMD_RM	0x11	/* read memory */
#define STM32_CMD_GO	0x21	/* go */
#define STM32_CMD_WM	0x31	/* write memory */
#define STM32_CMD_WM_NS	0x32	/* no-stretch write memory */
#define STM32_CMD_ER	0x43	/* erase */
#define STM32_CMD_EE	0x44	/* extended erase */
#define STM32_CMD_EE_NS	0x45	/* extended erase no-stretch */
#define STM32_CMD_WP	0x63	/* write protect */
#define STM32_CMD_WP_NS	0x64	/* write protect no-stretch */
#define STM32_CMD_UW	0x73	/* write unprotect */
#define STM32_CMD_UW_NS	0x74	/* write unprotect no-stretch */
#define STM32_CMD_RP	0x82	/* readout protect */
#define STM32_CMD_RP_NS	0x83	/* readout protect no-stretch */
#define STM32_CMD_UR	0x92	/* readout unprotect */
#define STM32_CMD_UR_NS	0x93	/* readout unprotect no-stretch */
#define STM32_CMD_CRC	0xA1	/* compute CRC */
#define STM32_CMD_ERR	0xFF	/* not a valid command */

#define STM32_RESYNC_TIMEOUT	35000	/* ms */
#define STM32_MASSERASE_TIMEOUT	35000	/* ms */
#define STM32_PAGEERASE_TIMEOUT	5000	/* ms */
#define STM32_BLKWRITE_TIMEOUT	1000	/* ms */
#define STM32_WUNPROT_TIMEOUT	1000	/* ms */
#define STM32_WPROT_TIMEOUT	1000	/* ms */
#define STM32_RPROT_TIMEOUT	1000	/* ms */

#define STM32_CMD_GET_LENGTH	17	/* bytes in the reply */

/* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0)
 * see ARMv7-M or ARMv6-M Architecture Reference Manual (table B3-8)
 * or "The definitive guide to the ARM Cortex-M3", section 14.4.
 */
static const uint8_t stm_reset_code[] = {
 0x01,0x49,		//	ldr     r1, [pc, #4] ; (<AIRCR_OFFSET>)
 0x02,0x4A,		//	ldr     r2, [pc, #8] ; (<AIRCR_RESET_VALUE>)
 0x0A,0x60,		//	str     r2, [r1, #0]
 0xfe,0xe7,		// 0:	b	0b
 0x0c,0xed,0x00,0xe0,	//	.word	0xe000ed0c <AIRCR_OFFSET> = NVIC AIRCR register address
 0x04,0x00,0xfa,0x05	//	.word	0x05fa0004 <AIRCR_RESET_VALUE> = VECTKEY | SYSRESETREQ
};

static const uint32_t stm_reset_code_length = sizeof(stm_reset_code);

/* RM0360, Empty check
 * On STM32F070x6 and STM32F030xC devices only, internal empty check flag is
 * implemented to allow easy programming of the virgin devices by the boot loader. This flag is
 * used when BOOT0 pin is defining Main Flash memory as the target boot space. When the
 * flag is set, the device is considered as empty and System memory (boot loader) is selected
 * instead of the Main Flash as a boot space to allow user to program the Flash memory.
 * This flag is updated only during Option bytes loading: it is set when the content of the
 * address 0x08000 0000 is read as 0xFFFF FFFF, otherwise it is cleared. It means a power
 * on or setting of OBL_LAUNCH bit in FLASH_CR register is needed to clear this flag after
 * programming of a virgin device to execute user code after System reset.
 */
static const uint8_t stm_obl_launch_code[] = {
 0x01,0x49,		//	ldr	r1, [pc, #4] ; (<FLASH_CR>)
 0x02,0x4A,		//	ldr	r2, [pc, #8] ; (<OBL_LAUNCH>)
 0x0A,0x60,		//	str	r2, [r1, #0]
 0xfe,0xe7,		// 0:	b	0b
 0x10,0x20,0x02,0x40,	// address: FLASH_CR = 40022010
 0x00,0x20,0x00,0x00,	// value: OBL_LAUNCH = 00002000
};

static const uint32_t stm_obl_launch_code_length = sizeof(stm_obl_launch_code);

/* RM0394, Empty check
 * On STM32L452 (and possibly all STM32L45xxx/46xxx) internal empty check flag is
 * implemented to allow easy programming of the virgin devices by the boot loader. This flag is
 * used when BOOT0 pin is defining Main Flash memory as the target boot space. When the
 * flag is set, the device is considered as empty and System memory (boot loader) is selected
 * instead of the Main Flash as a boot space to allow user to program the Flash memory.
 * This flag is updated only during Option bytes loading: it is set when the content of the
 * address 0x08000 0000 is read as 0xFFFF FFFF, otherwise it is cleared. It means a power
 * on or setting of OBL_LAUNCH bit in FLASH_CR register or a toggle of PEMPTY bit in FLASH_SR
 * register is needed to clear this flag after after programming of a virgin device to execute
 * user code after System reset.
 * In STM32L45xxx/46xxx the register FLASH_CR could be locked and a special SW sequence is
 * required for unlocking it. If a previous unsuccessful unlock has happened, a reset is
 * required before the unlock. Due to such complications, toggling the PEMPTY bit in FLASH_SR
 * seams the most reasonable choice.
 * The code below check first word in flash and flag PEMPTY. If they do not match, then it
 * toggles PEMPTY. At last, it resets.
 */

static const uint8_t stm_pempty_launch_code[] = {
 0x08,0x48,		//	ldr	r0, [pc, #32] ; (<BASE_FLASH>)
 0x00,0x68,		//	ldr	r0, [r0, #0]
 0x01,0x30,		//	adds	r0, #1
 0x41,0x1e,		//	subs	r1, r0, #1
 0x88,0x41,		//	sbcs	r0, r1

 0x07,0x49,		//	ldr	r1, [pc, #28] ; (<FLASH_SR>)
 0x07,0x4a,		//	ldr	r2, [pc, #28] ; (<PEMPTY_MASK>)
 0x0b,0x68,		//	ldr	r3, [r1, #0]
 0x13,0x40,		//	ands	r3, r2
 0x5c,0x1e,		//	subs	r4, r3, #1
 0xa3,0x41,		//	sbcs	r3, r4

 0x98,0x42,		//	cmp	r0, r3
 0x00,0xd1,		//	bne.n	1f

 0x0a,0x60,		//	str	r2, [r1, #0]

 0x04,0x48,		// 1:	ldr	r0, [pc, #16] ; (<AIRCR_OFFSET>)
 0x05,0x49,		//	ldr	r1, [pc, #16] ; (<AIRCR_RESET_VALUE>)
 0x01,0x60,		//	str	r1, [r0, #0]

 0xfe,0xe7,		// 0:	b.n	0b

 0x00,0x00,0x00,0x08,	//	.word 0x08000000 <BASE_FLASH>
 0x10,0x20,0x02,0x40,	//	.word 0x40022010 <FLASH_SR>
 0x00,0x00,0x02,0x00,	//	.word 0x00020000 <PEMPTY_MASK>
 0x0c,0xed,0x00,0xe0,	//	.word 0xe000ed0c <AIRCR_OFFSET> = NVIC AIRCR register address
 0x04,0x00,0xfa,0x05,	//	.word 0x05fa0004 <AIRCR_RESET_VALUE> = VECTKEY | SYSRESETREQ
};

static const uint32_t stm_pempty_launch_code_length = sizeof(stm_pempty_launch_code);

static void stm32_warn_stretching(const char *f) {
 fprintf(stderr, "Attention !!!\n");
 fprintf(stderr, "\tThis %s error could be caused by your I2C\n", f);
 fprintf(stderr, "\tcontroller not accepting \"clock stretching\"\n");
 fprintf(stderr, "\tas required by bootloader.\n");
 fprintf(stderr, "\tCheck \"I2C.txt\" in stm32flash source code.\n");
}

stm32::err_t stm32::get_ack(unsigned ms) const{
 uint8_t byte;
 Port::err_t p_err;
 unsigned t0;
 if (!(port.flags & Port::F_RETRY)) ms = 0;
 if (ms) t0=GetTickCount();
 for(;;) {
  p_err = port.read(&byte, 1);
  if (p_err == Port::ERR_TIMEDOUT && ms) {
   if (unsigned(GetTickCount()-t0) < ms) continue;
  }
  if (p_err) {
   fprintf(stderr, "Failed to read ACK byte\n");
   return ERR_UNKNOWN;
  }
  if (byte == STM32_ACK) return OK;
  if (byte == STM32_NACK) return ERR_NACK;
  if (byte != STM32_BUSY) {
   fprintf(stderr, "Got byte 0x%02x instead of ACK\n",byte);
   return ERR_UNKNOWN;
  }
 }
}

stm32::err_t stm32::send_command(uint8_t cmd, unsigned ms) const{
 uint8_t buf[2]={cmd,cmd^0xFF};
 if (port.write(buf,2)) {
  fprintf(stderr, "Failed to send command\n");
  return ERR_UNKNOWN;
 }
 err_t s_err = get_ack(ms);
 if (!s_err) return OK;
 fprintf(stderr,
   s_err == ERR_NACK ? "Got NACK from device on command 0x%02x\n"
   : "Unexpected reply from device on command 0x%02x\n", cmd);
 return ERR_UNKNOWN;
}

/* if we have lost sync, send a wrong command and expect a NACK */
stm32::err_t stm32::resync() const{
 uint8_t buf[2]={STM32_CMD_ERR,STM32_CMD_ERR^0xFF};
 unsigned t0 = GetTickCount();
 do{
  if (port.write(buf,2)) {
   Sleep(500);
   continue;
  }
  uint8_t ack;
  if (port.read(&ack,1)) continue;
  if (ack == ERR_NACK) return OK;
 }while (unsigned(GetTickCount()-t0)<STM32_RESYNC_TIMEOUT);
 return ERR_UNKNOWN;
}

/*
 * some command receive reply frame with variable length, and length is
 * embedded in reply frame itself.
 * We can guess the length, but if we guess wrong the protocol gets out
 * of sync.
 * Use resync for frame oriented interfaces (e.g. I2C) and byte-by-byte
 * read for byte oriented interfaces (e.g. UART).
 *
 * to run safely, data buffer should be allocated for 256+1 bytes
 *
 * len is value of the first byte in the frame.
 */
stm32::err_t stm32::guess_len_cmd(uint8_t cmd, uint8_t*data, unsigned len) const{
 Port::err_t p_err;
 if (send_command(cmd)) return ERR_UNKNOWN;
 if (port.flags & Port::F_BYTE) {
		/* interface is UART-like */
  p_err = port.read(data, 1);
  if (p_err) return ERR_UNKNOWN;
  len = data[0];
  p_err = port.read(data + 1, len + 1);
  if (p_err) return ERR_UNKNOWN;
  return OK;
 }
 p_err = port.read(data, len + 2);
 if (!p_err && len == data[0]) return OK;
 if (p_err) {
		/* restart with only one byte */
  if (resync()) return ERR_UNKNOWN;
  if (send_command(cmd)) return ERR_UNKNOWN;
  p_err = port.read(data, 1);
  if (p_err) return ERR_UNKNOWN;
 }
 fprintf(stderr, "Re sync (len = %d)\n", data[0]);
 if (resync()) return ERR_UNKNOWN;
 len = data[0];
 if (send_command(cmd)) return ERR_UNKNOWN;
 p_err = port.read(data, len + 2);
 if (p_err) return ERR_UNKNOWN;
 return OK;
}

/*
 * Some interface, e.g. UART, requires a specific init sequence to let STM32
 * autodetect the interface speed.
 * The sequence is only required one time after reset.
 * stm32flash has command line flag "-c" to prevent sending the init sequence
 * in case it was already sent before.
 * User can easily forget adding "-c". In this case the bootloader would
 * interpret the init sequence as part of a command message, then waiting for
 * the rest of the message blocking the interface.
 * This function sends the init sequence and, in case of timeout, recovers
 * the interface.
 */
stm32::err_t stm32::send_init_seq() const{
 uint8_t byte, cmd = STM32_CMD_INIT;
 Port::err_t p_err = port.write(&cmd, 1);
 if (p_err) {
  fprintf(stderr, "Failed to send init to device\n");
  return ERR_UNKNOWN;
 }
 p_err = port.read(&byte, 1);
 if (!p_err && byte == STM32_ACK) return OK;
 if (!p_err && byte == STM32_NACK) {
	/* We could get error later, but let's continue, for now. */
  fprintf(stderr, "Warning: the interface was not closed properly.\n");
  return OK;
 }
 if (p_err != Port::ERR_TIMEDOUT) {
  fprintf(stderr, "Failed to init device.\n");
  return ERR_UNKNOWN;
 }
	/* Check if previous STM32_CMD_INIT was taken as first byte
	 * of a command. Send a new byte, we should get back a NACK.
	 */
 p_err = port.write(&cmd, 1);
 if (p_err) {
  fprintf(stderr, "Failed to send init to device\n");
  return ERR_UNKNOWN;
 }
 p_err = port.read(&byte, 1);
 if (!p_err && byte == STM32_NACK) return OK;
 fprintf(stderr, "Failed to init device.\n");
 return ERR_UNKNOWN;
}

/* find newer command by higher code */
#define newer(prev, a) (((prev) == STM32_CMD_ERR) \
			? (a) \
			: (((prev) > (a)) ? (prev) : (a)))

stm32::stm32(Port&_port, const char init):okay(false),port(_port) {
 uint8_t len, val, buf[257];
 int i, new_cmds;
 if ((port.flags & Port::F_CMD_INIT) && init)
  if (send_init_seq()) return;
	/* get the version and read protection status  */
 if (send_command(STM32_CMD_GVR)) return;
	/* From AN, only UART bootloader returns 3 bytes */
 len = (port.flags & Port::F_GVR_ETX) ? 3 : 1;
 if (port.read(buf, len)) return;
 version = buf[0];
 option1 = (port.flags & Port::F_GVR_ETX) ? buf[1] : 0;
 option2 = (port.flags & Port::F_GVR_ETX) ? buf[2] : 0;
 if (get_ack()) return;
	/* get the bootloader information */
 len = STM32_CMD_GET_LENGTH;
 if (port.cmd_get_reply)
 for (i = 0; port.cmd_get_reply[i].length; i++)
 if (version == port.cmd_get_reply[i].version) {
  len = port.cmd_get_reply[i].length;
  break;
 }
 if (guess_len_cmd(STM32_CMD_GET, buf, len)) return;
 len = buf[0] + 1;
 bl_version = buf[1];
 new_cmds = 0;
 for (i = 1; i < len; i++) {
  val = buf[i + 1];
  switch (val) {
   case STM32_CMD_GET: cmd.get = val; break;
   case STM32_CMD_GVR: cmd.gvr = val; break;
   case STM32_CMD_GID: cmd.gid = val; break;
   case STM32_CMD_RM:  cmd.rm  = val; break;
   case STM32_CMD_GO:  cmd.go  = val; break;
   case STM32_CMD_WM_NS:
   case STM32_CMD_WM:	cmd.wm = newer(cmd.wm, val); break;
   case STM32_CMD_ER:
   case STM32_CMD_EE_NS:
   case STM32_CMD_EE:	cmd.er = newer(cmd.er, val); break;
   case STM32_CMD_WP_NS:
   case STM32_CMD_WP:	cmd.wp = newer(cmd.wp, val); break;
   case STM32_CMD_UW_NS:
   case STM32_CMD_UW:	cmd.uw = newer(cmd.uw, val); break;
   case STM32_CMD_RP_NS:
   case STM32_CMD_RP:	cmd.rp = newer(cmd.rp, val); break;
   case STM32_CMD_UR_NS:
   case STM32_CMD_UR:	cmd.ur = newer(cmd.ur, val); break;
   case STM32_CMD_CRC:	cmd.crc = newer(cmd.crc, val); break;
   default: if (!new_cmds++) fprintf(stderr,"GET returns unknown commands (0x%2x",val);
   else fprintf(stderr, ", 0x%2x", val);
  }
 }
 if (new_cmds) fprintf(stderr, ")\n");
 if (get_ack()) return;
 if (cmd.get == STM32_CMD_ERR
  || cmd.gvr == STM32_CMD_ERR
  || cmd.gid == STM32_CMD_ERR) {
  fprintf(stderr, "Error: bootloader did not returned correct information from GET command\n");
  return;
 }
	/* get the device ID */
 if (guess_len_cmd(cmd.gid, buf, 1)) return;
 len = buf[0] + 1;
 if (len < 2) {
  fprintf(stderr, "Only %d bytes sent in the PID, unknown/unsupported device\n", len);
  return;
 }
 pid = (buf[1] << 8) | buf[2];
 if (len > 2) {
  fprintf(stderr, "This bootloader returns %d extra bytes in PID:", len);
  for (i = 2; i <= len ; i++) fprintf(stderr, " %02x", buf[i]);
  fprintf(stderr, "\n");
 }
 if (get_ack()) return;
 dev = devices;
 while (dev->id && dev->id != pid) ++dev;
 if (!dev->id) {
  fprintf(stderr, "Unknown/unsupported device (Device ID: 0x%03x)\n", pid);
  return;
 }
 okay=true;
}

stm32::~stm32() {
}

stm32::err_t stm32::read_memory(uint32_t address, uint8_t data[], unsigned len) const{
 if (!len) return OK;
 if (len > 256) {
  fprintf(stderr, "Error: READ length limit at 256 bytes\n");
  return ERR_UNKNOWN;
 }
 if (cmd.rm == STM32_CMD_ERR) {
  fprintf(stderr, "Error: READ command not implemented in bootloader.\n");
  return ERR_NO_CMD;
 }
 if (send_command(cmd.rm)) return ERR_UNKNOWN;
 uint8_t buf[5]={
  uint8_t(address>>24),
  uint8_t(address>>16),
  uint8_t(address>>8),
  uint8_t(address),
  buf[0]^buf[1]^buf[2]^buf[3]};
 if (port.write(buf, 5)) return ERR_UNKNOWN;
 if (get_ack()) return ERR_UNKNOWN;
 if (send_command(uint8_t(len-1))) return ERR_UNKNOWN;
 if (port.read(data, len)) return ERR_UNKNOWN;
 return OK;
}

stm32::err_t stm32::write_memory(uint32_t address, const uint8_t data[], unsigned len) const{
 unsigned int aligned_len;
 if (!len) return OK;
 if (len > 256) {
  fprintf(stderr, "Error: READ length limit at 256 bytes\n");
  return ERR_UNKNOWN;
 }
	/* must be 32bit aligned */
 if (address & 0x3) {
  fprintf(stderr, "Error: WRITE address must be 4 byte aligned\n");
  return ERR_UNKNOWN;
 }
 if (cmd.wm == STM32_CMD_ERR) {
  fprintf(stderr, "Error: WRITE command not implemented in bootloader.\n");
  return ERR_NO_CMD;
 }
	/* send the address and checksum */
 uint8_t cs, buf[256 + 2];
 if (send_command(cmd.wm)) return ERR_UNKNOWN;
 buf[0] = uint8_t(address>>24);
 buf[1] = uint8_t(address>>16);
 buf[2] = uint8_t(address>>8);
 buf[3] = uint8_t(address);
 buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
 if (port.write(buf, 5)) return ERR_UNKNOWN;
 if (get_ack()) return ERR_UNKNOWN;
 aligned_len = (len + 3) & ~3;
 cs = aligned_len - 1;
 buf[0] = aligned_len - 1;
 size_t i;
 for (i = 0; i < len; i++) {
  cs ^= data[i];
  buf[i + 1] = data[i];
 }
	/* padding data */
 for (i = len; i < aligned_len; i++) {
  cs ^= 0xFF;
  buf[i + 1] = 0xFF;
 }
 buf[aligned_len + 1] = cs;
 if (port.write(buf, aligned_len + 2)) return ERR_UNKNOWN;
 stm32::err_t s_err = get_ack(STM32_BLKWRITE_TIMEOUT);
 if (s_err) {
  if ((port.flags & Port::F_STRETCH_W)
  && cmd.wm != STM32_CMD_WM_NS)
   stm32_warn_stretching("write");
  return ERR_UNKNOWN;
 }
 return OK;
}

stm32::err_t stm32::onecommand(const char*action,uint8_t cmd,unsigned ms,uint8_t stretchcmd) const{
 if (cmd == STM32_CMD_ERR) {
  fprintf(stderr, "Error: %s command not implemented in bootloader.\n",action);
  return ERR_NO_CMD;
 }
 if (send_command(cmd)) return ERR_UNKNOWN;
 stm32::err_t s_err = get_ack(ms);
 if (s_err == ERR_NACK) {
  fprintf(stderr, "Error: Failed to %s\n",action);
  return ERR_UNKNOWN;
 }
 if (s_err) {
  if ((port.flags & Port::F_STRETCH_W)
  && cmd != stretchcmd)
   stm32_warn_stretching(action);
  return ERR_UNKNOWN;
 }
 return OK;
}


stm32::err_t stm32::wunprot_memory() const{
 return onecommand("WRITE UNPROTECT",cmd.uw,STM32_WUNPROT_TIMEOUT,STM32_CMD_UW_NS);
}

stm32::err_t stm32::wprot_memory() const{
 return onecommand("WRITE PROTECT",cmd.wp,STM32_WPROT_TIMEOUT,STM32_CMD_WP_NS);
}

stm32::err_t stm32::runprot_memory() const{
 return onecommand("READOUT UNPROTECT",cmd.ur,STM32_MASSERASE_TIMEOUT,STM32_CMD_UR_NS);
}

stm32::err_t stm32::readprot_memory() const{
 return onecommand("READOUT PROTECT",cmd.rp,STM32_RPROT_TIMEOUT,STM32_CMD_RP_NS);
}

stm32::err_t stm32::mass_erase() const{
 stm32::err_t s_err;
 if (send_command(cmd.er)) {
  fprintf(stderr, "Can't initiate chip mass erase!\n");
  return ERR_UNKNOWN;
 }
	/* regular erase (0x43) */
 if (cmd.er == STM32_CMD_ER) {
  s_err = send_command(0xFF, STM32_MASSERASE_TIMEOUT);
  if (s_err) {
   if (port.flags & Port::F_STRETCH_W) stm32_warn_stretching("mass erase");
   return ERR_UNKNOWN;
  }
  return OK;
 }
	/* extended erase */
 uint8_t buf[3]={0xFF,0xFF,0};	/* 0xFFFF the magic number for mass erase; checksum */
 if (port.write(buf, 3)) {
  fprintf(stderr, "Mass erase error.\n");
  return ERR_UNKNOWN;
 }
 s_err = get_ack(STM32_MASSERASE_TIMEOUT);
 if (s_err) {
  fprintf(stderr, "Mass erase failed. Try specifying the number of pages to be erased.\n");
  if ((port.flags & Port::F_STRETCH_W)
  && cmd.er != STM32_CMD_EE_NS) stm32_warn_stretching("mass erase");
  return ERR_UNKNOWN;
 }
 return OK;
}

stm32::err_t stm32::pages_erase(uint32_t spage, uint32_t pages) const{
 stm32::err_t s_err;
	/* The erase command reported by the bootloader is either 0x43, 0x44 or 0x45 */
	/* 0x44 is Extended Erase, a 2 byte based protocol and needs to be handled differently. */
	/* 0x45 is clock no-stretching version of Extended Erase for I2C port. */
 if (send_command(cmd.er)) {
  fprintf(stderr, "Can't initiate chip mass erase!\n");
  return ERR_UNKNOWN;
 }
	/* regular erase (0x43) */
 int i = 0;
 uint8_t cs = 0;
 if (cmd.er == STM32_CMD_ER) {
  uint8_t*buf=new uint8_t[1+pages+1];
  if (!buf) return ERR_UNKNOWN;
  buf[i++] = uint8_t(pages-1);
  cs ^= (pages-1);
  for (uint32_t pg_num = spage; pg_num < (pages + spage); pg_num++) {
   buf[i++] = uint8_t(pg_num);
   cs ^= pg_num;
  }
  buf[i++] = cs;
  Port::err_t p_err = port.write(buf, i);
  delete[]buf;
  if (p_err) {
   fprintf(stderr, "Erase failed.\n");
   return ERR_UNKNOWN;
  }
  s_err = get_ack(pages * STM32_PAGEERASE_TIMEOUT);
  if (s_err) {
   if (port.flags & Port::F_STRETCH_W) stm32_warn_stretching("erase");
   return ERR_UNKNOWN;
  }
  return OK;
 }
	/* extended erase */
 uint8_t*buf=new uint8_t[2+2*pages+1];
 if (!buf) return ERR_UNKNOWN;
	/* Number of pages to be erased - 1, two bytes, MSB first */
 uint8_t pg_byte = uint8_t((pages-1)>>8);
 buf[i++] = pg_byte;
 cs ^= pg_byte;
 pg_byte = uint8_t(pages-1);
 buf[i++] = pg_byte;
 cs ^= pg_byte;
 for (uint32_t pg_num = spage; pg_num < spage + pages; pg_num++) {
  pg_byte = uint8_t(pg_num>>8);
  cs ^= pg_byte;
  buf[i++] = pg_byte;
  pg_byte = uint8_t(pg_num);
  cs ^= pg_byte;
  buf[i++] = pg_byte;
 }
 buf[i++] = cs;
 Port::err_t p_err = port.write(buf, i);
 delete[]buf;
 if (p_err) {
  fprintf(stderr, "Page-by-page erase error.\n");
  return ERR_UNKNOWN;
 }
 s_err = get_ack(pages*STM32_PAGEERASE_TIMEOUT);
 if (s_err) {
  fprintf(stderr, "Page-by-page erase failed. Check the maximum pages your device supports.\n");
  if ((port.flags & Port::F_STRETCH_W)
  && cmd.er != STM32_CMD_EE_NS) stm32_warn_stretching("erase");
  return ERR_UNKNOWN;
 }
 return OK;
}

stm32::err_t stm32::erase_memory(uint32_t spage, uint32_t pages) const{
 uint32_t n;
 stm32::err_t s_err;
 if (!pages || spage > stm32::MAX_PAGES ||
    ((pages != stm32::MASS_ERASE) && ((spage + pages) > stm32::MAX_PAGES)))
		return OK;

 if (cmd.er == STM32_CMD_ERR) {
  fprintf(stderr, "Error: ERASE command not implemented in bootloader.\n");
  return ERR_NO_CMD;
 }
 if (pages == stm32::MASS_ERASE) {
		/* Not all chips support mass erase.
		 * Mass erase can be obtained executing a "readout protect"
		 * followed by "readout un-protect". This method is not
		 * suggested because can hang the target if a debug SWD/JTAG
		 * is connected. When the target enters in "readout
		 * protection" mode it will consider the debug connection as
		 * a tentative of intrusion and will hang.
		 * Erasing the flash page-by-page is the safer way to go.
		 */
  if (!(dev->flags & F_NO_ME)) return mass_erase();
  pages = dev->flash_addr_to_page(dev->fl_end,true);
 }
	/* Some device, like STM32L152, cannot erase more than 512 pages in
	 * one command. Split the call.
	 */
 while (pages) {
  n = (pages <= 512) ? pages : 512;
  s_err = pages_erase(spage, n);
  if (s_err) return s_err;
  spage += n;
  pages -= n;
 }
 return OK;
}

stm32::err_t stm32::run_raw_code(uint32_t target_address, const uint8_t *code, uint32_t code_size) const{
 uint32_t stack_le = le_u32(0x20002000);
 uint32_t code_address_le = le_u32(target_address + 8 + 1); // thumb mode address (!)
 uint32_t length = code_size + 8;
 uint32_t address, w;

	/* Must be 32-bit aligned */
 if (target_address & 0x3) {
  fprintf(stderr, "Error: code address must be 4 byte aligned\n");
  return ERR_UNKNOWN;
 }
 uint8_t*mem = new uint8_t[length];
 if (!mem) return ERR_UNKNOWN;
 memcpy(mem, &stack_le, sizeof(uint32_t));
 memcpy(mem + 4, &code_address_le, sizeof(uint32_t));
 memcpy(mem + 8, code, code_size);
 uint8_t*pos = mem;
 address = target_address;
 while (length > 0) {
  w = length > 256 ? 256 : length;
  if (write_memory(address, pos, w)) {
   delete[]mem;
   return ERR_UNKNOWN;
  }
  address += w;
  pos += w;
  length -= w;
 }
 delete[]mem;
 return go(target_address);
}

stm32::err_t stm32::go(uint32_t address) const{
 if (cmd.go == STM32_CMD_ERR) {
  fprintf(stderr, "Error: GO command not implemented in bootloader.\n");
  return ERR_NO_CMD;
 }
 if (send_command(cmd.go)) return ERR_UNKNOWN;
 uint8_t buf[5]={
  uint8_t(address>>24),
  uint8_t(address>>16),
  uint8_t(address>>8),
  uint8_t(address),
  buf[0]^buf[1]^buf[2]^buf[3]};
 if (port.write(buf, 5)) return ERR_UNKNOWN;
 if (get_ack())	return ERR_UNKNOWN;
 return OK;
}

stm32::err_t stm32::reset_device() const{
 uint32_t target_address = dev->ram_start;
 if (dev->flags & F_OBLL)
		/* set the OBL_LAUNCH bit to reset device (see RM0360, 2.5) */
   return run_raw_code(target_address, stm_obl_launch_code, stm_obl_launch_code_length);
 if (dev->flags & F_PEMPTY)
		/* clear the PEMPTY bit to reset the device (see RM0394) */
   return run_raw_code(target_address, stm_pempty_launch_code, stm_pempty_launch_code_length);
 return run_raw_code(target_address, stm_reset_code, stm_reset_code_length);
}

stm32::err_t stm32::crc_memory(uint32_t address, uint32_t length, uint32_t&crc) const{
 if (address&3 || length&3) {
  fprintf(stderr, "Start and end addresses must be 4 byte aligned\n");
  return ERR_UNKNOWN;
 }
 if (cmd.crc == STM32_CMD_ERR) {
  fprintf(stderr, "Error: CRC command not implemented in bootloader.\n");
  return ERR_NO_CMD;
 }
 if (send_command(cmd.crc)) return ERR_UNKNOWN;
 uint8_t buf[5]={
  uint8_t(address>>24),
  uint8_t(address>>16),
  uint8_t(address>>8),
  uint8_t(address),
  buf[0]^buf[1]^buf[2]^buf[3]};
 if (port.write(buf, 5)) return ERR_UNKNOWN;
 if (get_ack()) return ERR_UNKNOWN;
 buf[0] = uint8_t(length>>24);
 buf[1] = uint8_t(length>>16);
 buf[2] = uint8_t(length>>8);
 buf[3] = uint8_t(length);
 buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
 if (port.write(buf, 5)) return ERR_UNKNOWN;
 if (get_ack()) return ERR_UNKNOWN;
 if (get_ack()) return ERR_UNKNOWN;
 if (port.read(buf, 5))	return ERR_UNKNOWN;
 if (buf[4] != (buf[0]^buf[1]^buf[2]^buf[3])) return ERR_UNKNOWN;
 crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
 return OK;
}

/*
 * CRC computed by STM32 is similar to the standard crc32_be()
 * implemented, for example, in Linux kernel in ./lib/crc32.c
 * But STM32 computes it on units of 32 bits word and swaps the
 * bytes of the word before the computation.
 * Due to byte swap, I cannot use any CRC available in existing
 * libraries, so here is a simple not optimized implementation.
 */
#define CRCPOLY_BE	0x04c11db7
#define CRC_INIT_VALUE	0xFFFFFFFF
uint32_t stm32::sw_crc(uint32_t crc, const uint8_t *buf, unsigned len) {
 uint32_t data;
 if (len & 0x3) {
  fprintf(stderr, "Buffer length must be multiple of 4 bytes\n");
  return 0;
 }
 while (len) {
  data = *buf++;
  data |= *buf++ << 8;
  data |= *buf++ << 16;
  data |= *buf++ << 24;
  len -= 4;
  crc ^= data;
  for (int i = 0; i < 32; i++)
  crc = crc & 0x80000000 ? crc<<1 ^ CRCPOLY_BE : crc<<1;
 }
 return crc;
}

stm32::err_t stm32::crc_wrapper(uint32_t address, uint32_t length, uint32_t&crc) const{
 uint8_t buf[256];
 uint32_t start, total_len, len, current_crc;
 if (address&3 || length&3) {
  fprintf(stderr, "Start and end addresses must be 4 byte aligned\n");
  return ERR_UNKNOWN;
 }
 if (cmd.crc != STM32_CMD_ERR) return crc_memory(address, length, crc);

 start = address;
 total_len = length;
 current_crc = CRC_INIT_VALUE;
 while (length) {
  len = length > 256 ? 256 : length;
  if (read_memory(address, buf, len)) {
   fprintf(stderr,"Failed to read memory at address 0x%08x, target write-protected?\n",address);
   return ERR_UNKNOWN;
  }
  current_crc = sw_crc(current_crc, buf, len);
  length -= len;
  address += len;
  fprintf(stderr,"\rCRC address 0x%08x (%.2f%%) ",address,
   (100.0f / (float)total_len) * (float)(address - start));
  fflush(stderr);
 }
 fprintf(stderr, "Done.\n");
 crc = current_crc;
 return OK;
}

/*if 0
// Utils:
uint32_t be_u32(const uint32_t v) {
 return	(v&0xFF000000)>>24
      | (v&0x00FF0000)>> 8
      | (v&0x0000FF00)<< 8
      |	(v&0x000000FF)<<24;
}
*/
Vorgefundene Kodierung: ASCII (7 bit)2