summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Shadura <andrewsh@debian.org>2014-10-06 08:35:56 +0200
committerAndrew Shadura <andrewsh@debian.org>2016-02-15 12:51:18 +0300
commit9de12366580cd1fe03b74bdda97541ead108bdd6 (patch)
treeb56b5dde1f8bb10e89f0f07ab19f5be5f3e0d254
parent2cb4f8fe0dd35b30c6d6c0071bed77c6eba70553 (diff)
parentfbbebf2cfecf4c184a5076c07ba87e4224f008fc (diff)
Imported Debian patch 0.4-1
-rw-r--r--Android.mk20
-rw-r--r--HOWTO35
-rw-r--r--I2C.txt94
-rw-r--r--Makefile10
-rw-r--r--debian/changelog6
-rw-r--r--dev_table.c70
-rw-r--r--i2c.c209
-rw-r--r--init.c28
-rw-r--r--init.h6
-rw-r--r--main.c361
-rw-r--r--parsers/Android.mk6
-rw-r--r--parsers/hex.c8
-rw-r--r--port.c59
-rw-r--r--port.h75
-rw-r--r--protocol.txt19
-rw-r--r--serial.h37
-rw-r--r--serial_posix.c329
-rw-r--r--serial_w32.c343
-rw-r--r--stm32.c1065
-rw-r--r--stm32.h40
-rw-r--r--stm32flash.1110
-rw-r--r--utils.c4
22 files changed, 2180 insertions, 754 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..7be3d00
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,20 @@
+TOP_LOCAL_PATH := $(call my-dir)
+
+include $(call all-named-subdir-makefiles, parsers)
+
+LOCAL_PATH := $(TOP_LOCAL_PATH)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := stm32flash
+LOCAL_SRC_FILES := \
+ dev_table.c \
+ i2c.c \
+ init.c \
+ main.c \
+ port.c \
+ serial_common.c \
+ serial_platform.c \
+ stm32.c \
+ utils.c
+LOCAL_STATIC_LIBRARIES := libparsers
+include $(BUILD_EXECUTABLE)
diff --git a/HOWTO b/HOWTO
new file mode 100644
index 0000000..d8f32eb
--- /dev/null
+++ b/HOWTO
@@ -0,0 +1,35 @@
+Add new interfaces:
+=====================================================================
+Current version 0.4 supports the following interfaces:
+- UART Windows (either "COMn" and "\\.\COMn");
+- UART posix/Linux (e.g. "/dev/ttyUSB0");
+- I2C Linux through standard driver "i2c-dev" (e.g. "/dev/i2c-n").
+
+Starting from version 0.4, the back-end of stm32flash is modular and
+ready to be expanded to support new interfaces.
+I'm planning adding SPI on Linux through standard driver "spidev".
+You are invited to contribute with more interfaces.
+
+To add a new interface you need to add a new file, populate the struct
+port_interface (check at the end of files i2c.c, serial_posix.c and
+serial_w32.c) and provide the relative functions to operate on the
+interface: open/close, read/write, get_cfg_str and the optional gpio.
+The include the new drive in Makefile and register the new struct
+port_interface in file port.c in struct port_interface *ports[].
+
+There are several USB-I2C adapter in the market, each providing its
+own libraries to communicate with the I2C bus.
+Could be interesting to provide as back-end a bridge between stm32flash
+and such libraries (I have no plan on this item).
+
+
+Add new STM32 devices:
+=====================================================================
+Add a new line in file dev_table.c, in table devices[].
+The fields of the table are listed in stm32.h, struct stm32_dev.
+
+
+Cross compile on Linux host for Windows target with MinGW:
+=====================================================================
+I'm using a 64 bit Arch Linux machines, and I usually run:
+ make CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar
diff --git a/I2C.txt b/I2C.txt
new file mode 100644
index 0000000..4c05ff6
--- /dev/null
+++ b/I2C.txt
@@ -0,0 +1,94 @@
+About I2C back-end communication in stm32flash
+==========================================================================
+
+Starting from version v0.4, beside the serial communication port,
+stm32flash adds support for I2C port to talk with STM32 bootloader.
+
+The current I2C back-end supports only the API provided by Linux kernel
+driver "i2c-dev", so only I2C controllers with Linux kernel driver can be
+used.
+In Linux source code, most of the drivers for I2C and SMBUS controllers
+are in
+ ./drivers/i2c/busses/
+Only I2C is supported by STM32 bootloader, so check the section below
+about SMBUS.
+No I2C support for Windows is available in stm32flash v0.4.
+
+Thanks to the new modular back-end, stm32flash can be easily extended to
+support new back-ends and API. Check HOWTO file in stm32flash source code
+for details.
+
+In the market there are several USB-to-I2C dongles; most of them are not
+supported by kernel drivers. Manufacturer provide proprietary userspace
+libraries using not standardized API.
+These API and dongles could be supported in feature versions.
+
+There are currently 3 versions of STM32 bootloader for I2C communications:
+- v1.0 using I2C clock stretching synchronization between host and STM32;
+- v1.1 superset of v1.0, adds non stretching commands;
+- v1.2 superset of v1.1, adds CRC command and compatibility with i2cdetect.
+Details in ST application note AN2606.
+All the bootloaders above are tested and working with stm32flash.
+
+
+SMBUS controllers
+==========================================================================
+
+Almost 50% of the drivers in Linux source code folder
+ ./drivers/i2c/busses/
+are for controllers that "only" support SMBUS protocol. They can NOT
+operate with STM32 bootloader.
+To identify if your controller supports I2C, use command:
+ i2cdetect -F n
+where "n" is the number of the I2C interface (e.g. n=3 for "/dev/i2c-3").
+Controllers that supports I2C will report
+ I2C yes
+Controller that support both I2C and SMBUS are ok.
+
+If you are interested on details about SMBUS protocol, you can download
+the current specs from
+ http://smbus.org/specs/smbus20.pdf
+and you can read the files in Linux source code
+ ./Documentation/i2c/i2c-protocol
+ ./Documentation/i2c/smbus-protocol
+
+
+About bootloader v1.0
+==========================================================================
+
+Version v1.0 can have issues with some I2C controllers due to use of clock
+stretching during commands that require long operations, like flash erase
+and programming.
+
+Clock stretching is a technique to synchronize host and I2C device. When
+I2C device wants to force a delay in the communication, it push "low" the
+I2C clock; the I2C controller detects it and waits until I2C clock returns
+"high".
+Most I2C controllers set a "timeout" for clock stretching, ranging from
+few milli-seconds to seconds depending on specific HW or SW driver.
+
+It is possible that the timeout in your I2C controller is smaller than the
+delay required for flash erase or programming. In this case the I2C
+controller will timeout and report error to stm32flash.
+There is no possibility for stm32flash to retry, so it can only signal the
+error and exit.
+
+To by-pass the issue with bootloader v1.0 you can modify the kernel driver
+of your I2C controller. Not an easy job, since every controller has its own
+way to handle the timeout.
+
+In my case I'm using the I2C controller integrated in the VGA port of my
+laptop HP EliteBook 8460p. I built the 0.25$ VGA-to-I2C adapter reported in
+ http://www.paintyourdragon.com/?p=43
+To change the timeout of the I2C controller I had to modify the kernel file
+ drivers/gpu/drm/radeon/radeon_i2c.c
+line 969
+- i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */
++ i2c->bit.timeout = msecs_to_jiffies(5000); /* 5s for STM32 */
+and recompile it.
+Then
+ $> modprobe i2c-dev
+ $> chmod 666 /dev/i2c-7
+ #> stm32flash -a 0x39 /dev/i2c-7
+
+2014-09-16 Antonio Borneo
diff --git a/Makefile b/Makefile
index 2ebec85..0328d55 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,15 @@ CFLAGS += -Wall -g
INSTALL = install
-OBJS = main.o utils.o stm32.o serial_common.o serial_platform.o init.o
+OBJS = dev_table.o \
+ i2c.o \
+ init.o \
+ main.o \
+ port.o \
+ serial_common.o \
+ serial_platform.o \
+ stm32.o \
+ utils.o
LIBOBJS = parsers/parsers.a
diff --git a/debian/changelog b/debian/changelog
index 889b1df..73fdd97 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+stm32flash (0.4-1) unstable; urgency=medium
+
+ * New upstream release (Closes: #763972).
+
+ -- Andrew Shadura <andrewsh@debian.org> Mon, 06 Oct 2014 08:35:56 +0200
+
stm32flash (0.3+dfsg-1) unstable; urgency=medium
* Initial release (Closes: #735917).
diff --git a/dev_table.c b/dev_table.c
new file mode 100644
index 0000000..399cd9d
--- /dev/null
+++ b/dev_table.c
@@ -0,0 +1,70 @@
+/*
+ stm32flash - Open Source ST STM32 flash program for *nix
+ Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com>
+ Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "stm32.h"
+
+/*
+ * Device table, corresponds to the "Bootloader device-dependant parameters"
+ * table in ST document AN2606.
+ * Note that the option bytes upper range is inclusive!
+ */
+const stm32_dev_t devices[] = {
+ /* F0 */
+ {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800},
+ {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800},
+ {0x445, "STM32F042xx" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800},
+ {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800},
+ /* F1 */
+ {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
+ {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
+ {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
+ {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
+ {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
+ {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800},
+ {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800},
+ /* Note that F2 and F4 devices have sectors of different page sizes
+ and only the first sectors (of one page size) are included here */
+ /* F2 */
+ {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF},
+ /* F3 */
+ {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
+ {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
+ {0x439, "STM32F302x4(6/8)" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
+ {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
+ /* F4 */
+ {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF},
+ /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */
+ {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF},
+ {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF},
+ {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF},
+ /* L0 */
+ {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, 128, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000},
+ /* L1 */
+ {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000},
+ {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000},
+ {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000},
+ {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000},
+ {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000},
+ /* These are not (yet) in AN2606: */
+ {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
+ {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0},
+ {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0},
+ {0x0}
+};
diff --git a/i2c.c b/i2c.c
new file mode 100644
index 0000000..9079e01
--- /dev/null
+++ b/i2c.c
@@ -0,0 +1,209 @@
+/*
+ stm32flash - Open Source ST STM32 flash program for *nix
+ Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "serial.h"
+#include "port.h"
+
+
+#if defined(__WIN32__) || defined(__CYGWIN__) || defined(__APPLE__)
+
+static port_err_t i2c_open(struct port_interface *port,
+ struct port_options *ops)
+{
+ return PORT_ERR_NODEV;
+}
+
+struct port_interface port_i2c = {
+ .name = "i2c",
+ .open = i2c_open,
+};
+
+#else
+
+#ifdef __ANDROID__
+#define I2C_SLAVE 0x0703 /* Use this slave address */
+#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */
+/* To determine what functionality is present */
+#define I2C_FUNC_I2C 0x00000001
+#else
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#endif
+
+#include <sys/ioctl.h>
+
+struct i2c_priv {
+ int fd;
+ int addr;
+};
+
+static port_err_t i2c_open(struct port_interface *port,
+ struct port_options *ops)
+{
+ struct i2c_priv *h;
+ int fd, addr, ret;
+ unsigned long funcs;
+
+ /* 1. check device name match */
+ if (strncmp(ops->device, "/dev/i2c-", strlen("/dev/i2c-")))
+ return PORT_ERR_NODEV;
+
+ /* 2. check options */
+ addr = ops->bus_addr;
+ if (addr < 0x03 || addr > 0x77) {
+ fprintf(stderr, "I2C address out of range [0x03-0x77]\n");
+ return PORT_ERR_UNKNOWN;
+ }
+
+ /* 3. open it */
+ h = calloc(sizeof(*h), 1);
+ if (h == NULL) {
+ fprintf(stderr, "End of memory\n");
+ return PORT_ERR_UNKNOWN;
+ }
+ fd = open(ops->device, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open special file \"%s\"\n",
+ ops->device);
+ free(h);
+ return PORT_ERR_UNKNOWN;
+ }
+
+ /* 3.5. Check capabilities */
+ ret = ioctl(fd, I2C_FUNCS, &funcs);
+ if (ret < 0) {
+ fprintf(stderr, "I2C ioctl(funcs) error %d\n", errno);
+ close(fd);
+ free(h);
+ return PORT_ERR_UNKNOWN;
+ }
+ if ((funcs & I2C_FUNC_I2C) == 0) {
+ fprintf(stderr, "Error: controller is not I2C, only SMBUS.\n");
+ close(fd);
+ free(h);
+ return PORT_ERR_UNKNOWN;
+ }
+
+ /* 4. set options */
+ ret = ioctl(fd, I2C_SLAVE, addr);
+ if (ret < 0) {
+ fprintf(stderr, "I2C ioctl(slave) error %d\n", errno);
+ close(fd);
+ free(h);
+ return PORT_ERR_UNKNOWN;
+ }
+
+ h->fd = fd;
+ h->addr = addr;
+ port->private = h;
+ return PORT_ERR_OK;
+}
+
+static port_err_t i2c_close(struct port_interface *port)
+{
+ struct i2c_priv *h;
+
+ h = (struct i2c_priv *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+ close(h->fd);
+ free(h);
+ port->private = NULL;
+ return PORT_ERR_OK;
+}
+
+static port_err_t i2c_read(struct port_interface *port, void *buf,
+ size_t nbyte)
+{
+ struct i2c_priv *h;
+ int ret;
+
+ h = (struct i2c_priv *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+ ret = read(h->fd, buf, nbyte);
+ if (ret != nbyte)
+ return PORT_ERR_UNKNOWN;
+ return PORT_ERR_OK;
+}
+
+static port_err_t i2c_write(struct port_interface *port, void *buf,
+ size_t nbyte)
+{
+ struct i2c_priv *h;
+ int ret;
+
+ h = (struct i2c_priv *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+ ret = write(h->fd, buf, nbyte);
+ if (ret != nbyte)
+ return PORT_ERR_UNKNOWN;
+ return PORT_ERR_OK;
+}
+
+static port_err_t i2c_gpio(struct port_interface *port, serial_gpio_t n,
+ int level)
+{
+ return PORT_ERR_OK;
+}
+
+static const char *i2c_get_cfg_str(struct port_interface *port)
+{
+ struct i2c_priv *h;
+ static char str[11];
+
+ h = (struct i2c_priv *)port->private;
+ if (h == NULL)
+ return "INVALID";
+ snprintf(str, sizeof(str), "addr 0x%2x", h->addr);
+ return str;
+}
+
+static struct varlen_cmd i2c_cmd_get_reply[] = {
+ {0x10, 11},
+ {0x11, 17},
+ {0x12, 18},
+ { /* sentinel */ }
+};
+
+struct port_interface port_i2c = {
+ .name = "i2c",
+ .flags = PORT_STRETCH_W,
+ .open = i2c_open,
+ .close = i2c_close,
+ .read = i2c_read,
+ .write = i2c_write,
+ .gpio = i2c_gpio,
+ .cmd_get_reply = i2c_cmd_get_reply,
+ .get_cfg_str = i2c_get_cfg_str,
+};
+
+#endif
diff --git a/init.c b/init.c
index 906522c..77a571b 100644
--- a/init.c
+++ b/init.c
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <fcntl.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -29,6 +30,7 @@
#include "init.h"
#include "serial.h"
#include "stm32.h"
+#include "port.h"
struct gpio_list {
struct gpio_list *next;
@@ -105,7 +107,7 @@ static int release_gpio(int n)
return write_to("/sys/class/gpio/unexport", num);
}
-static int gpio_sequence(serial_t *serial, const char *s, size_t l)
+static int gpio_sequence(struct port_interface *port, const char *s, size_t l)
{
struct gpio_list *gpio_to_release = NULL, *to_free;
int ret, level, gpio;
@@ -154,7 +156,7 @@ static int gpio_sequence(serial_t *serial, const char *s, size_t l)
}
}
if (gpio < 0)
- ret = (serial_gpio(serial, -gpio, level) == SERIAL_ERR_OK);
+ ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK);
else
ret = drive_gpio(gpio, level, &gpio_to_release);
usleep(100000);
@@ -170,7 +172,7 @@ static int gpio_sequence(serial_t *serial, const char *s, size_t l)
return ret;
}
-static int gpio_bl_entry(serial_t *serial, const char *seq)
+static int gpio_bl_entry(struct port_interface *port, const char *seq)
{
char *s;
@@ -179,12 +181,12 @@ static int gpio_bl_entry(serial_t *serial, const char *seq)
s = strchr(seq, ':');
if (s == NULL)
- return gpio_sequence(serial, seq, strlen(seq));
+ return gpio_sequence(port, seq, strlen(seq));
- return gpio_sequence(serial, seq, s - seq);
+ return gpio_sequence(port, seq, s - seq);
}
-static int gpio_bl_exit(serial_t *serial, const char *seq)
+static int gpio_bl_exit(struct port_interface *port, const char *seq)
{
char *s;
@@ -195,21 +197,23 @@ static int gpio_bl_exit(serial_t *serial, const char *seq)
if (s == NULL || s[1] == '\0')
return 1;
- return gpio_sequence(serial, s + 1, strlen(s + 1));
+ return gpio_sequence(port, s + 1, strlen(s + 1));
}
-int init_bl_entry(serial_t *serial, const char *seq)
+int init_bl_entry(struct port_interface *port, const char *seq)
{
if (seq)
- return gpio_bl_entry(serial, seq);
+ return gpio_bl_entry(port, seq);
return 1;
}
-int init_bl_exit(stm32_t *stm, serial_t *serial, const char *seq)
+int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq)
{
if (seq)
- return gpio_bl_exit(serial, seq);
+ return gpio_bl_exit(port, seq);
- return stm32_reset_device(stm);
+ if (stm32_reset_device(stm) != STM32_ERR_OK)
+ return 0;
+ return 1;
}
diff --git a/init.h b/init.h
index 1acb027..6075b51 100644
--- a/init.h
+++ b/init.h
@@ -22,10 +22,10 @@
#ifndef _INIT_H
#define _INIT_H
-#include "serial.h"
#include "stm32.h"
+#include "port.h"
-int init_bl_entry(serial_t *serial, const char *seq);
-int init_bl_exit(stm32_t *stm, serial_t *serial, const char *seq);
+int init_bl_entry(struct port_interface *port, const char *seq);
+int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq);
#endif
diff --git a/main.c b/main.c
index 8c3f7ae..5105d21 100644
--- a/main.c
+++ b/main.c
@@ -33,38 +33,45 @@
#include "serial.h"
#include "stm32.h"
#include "parsers/parser.h"
+#include "port.h"
#include "parsers/binary.h"
#include "parsers/hex.h"
-#define VERSION "0.3"
+#define VERSION "0.4"
/* device globals */
-serial_t *serial = NULL;
stm32_t *stm = NULL;
void *p_st = NULL;
parser_t *parser = NULL;
/* settings */
-char *device = NULL;
-serial_baud_t baudRate = SERIAL_BAUD_57600;
-char *serial_mode = "8e1";
+struct port_options port_opts = {
+ .device = NULL,
+ .baudRate = SERIAL_BAUD_57600,
+ .serial_mode = "8e1",
+ .bus_addr = 0,
+ .rx_frame_max = STM32_MAX_RX_FRAME,
+ .tx_frame_max = STM32_MAX_TX_FRAME,
+};
int rd = 0;
int wr = 0;
int wu = 0;
int rp = 0;
int ur = 0;
int eraseOnly = 0;
+int crc = 0;
int npages = 0;
int spage = 0;
+int no_erase = 0;
char verify = 0;
int retry = 10;
char exec_flag = 0;
uint32_t execute = 0;
char init_flag = 1;
char force_binary = 0;
-char reset_flag = 1;
+char reset_flag = 0;
char *filename;
char *gpio_seq = NULL;
uint32_t start_addr = 0;
@@ -74,8 +81,42 @@ uint32_t readwrite_len = 0;
int parse_options(int argc, char *argv[]);
void show_help(char *name);
+static int is_addr_in_ram(uint32_t addr)
+{
+ return addr >= stm->dev->ram_start && addr < stm->dev->ram_end;
+}
+
+static int is_addr_in_flash(uint32_t addr)
+{
+ return addr >= stm->dev->fl_start && addr < stm->dev->fl_end;
+}
+
+static int flash_addr_to_page_floor(uint32_t addr)
+{
+ if (!is_addr_in_flash(addr))
+ return 0;
+
+ return (addr - stm->dev->fl_start) / stm->dev->fl_ps;
+}
+
+static int flash_addr_to_page_ceil(uint32_t addr)
+{
+ if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end))
+ return 0;
+
+ return (addr + stm->dev->fl_ps - 1 - stm->dev->fl_start)
+ / stm->dev->fl_ps;
+}
+
+static uint32_t flash_page_to_addr(int page)
+{
+ return stm->dev->fl_start + page * stm->dev->fl_ps;
+}
+
int main(int argc, char* argv[]) {
+ struct port_interface *port = NULL;
int ret = 1;
+ stm32_err_t s_err;
parser_err_t perr;
FILE *diag = stdout;
@@ -134,31 +175,23 @@ int main(int argc, char* argv[]) {
}
}
- serial = serial_open(device);
- if (!serial) {
- fprintf(stderr, "Failed to open serial port: ");
- perror(device);
+ if (port_open(&port_opts, &port) != PORT_ERR_OK) {
+ fprintf(stderr, "Failed to open port: %s\n", port_opts.device);
goto close;
}
- if (serial_setup(
- serial,
- baudRate,
- serial_get_bits(serial_mode),
- serial_get_parity(serial_mode),
- serial_get_stopbit(serial_mode)
- ) != SERIAL_ERR_OK) {
- perror(device);
+ fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port));
+ if (init_flag && init_bl_entry(port, gpio_seq) == 0)
+ goto close;
+ stm = stm32_init(port, init_flag);
+ if (!stm)
goto close;
- }
-
- fprintf(diag, "Serial Config: %s\n", serial_get_setup_str(serial));
- if (init_flag && init_bl_entry(serial, gpio_seq) == 0) goto close;
- if (!(stm = stm32_init(serial, init_flag))) goto close;
fprintf(diag, "Version : 0x%02x\n", stm->bl_version);
- fprintf(diag, "Option 1 : 0x%02x\n", stm->option1);
- fprintf(diag, "Option 2 : 0x%02x\n", stm->option2);
+ if (port->flags & PORT_GVR_ETX) {
+ fprintf(diag, "Option 1 : 0x%02x\n", stm->option1);
+ fprintf(diag, "Option 2 : 0x%02x\n", stm->option2);
+ }
fprintf(diag, "Device ID : 0x%04x (%s)\n", stm->pid, stm->dev->name);
fprintf(diag, "- RAM : %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000);
fprintf(diag, "- Flash : %dKiB (sector size: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps);
@@ -169,38 +202,84 @@ int main(int argc, char* argv[]) {
uint32_t addr, start, end;
unsigned int len;
int failed = 0;
+ int first_page, num_pages;
+
+ /*
+ * Cleanup addresses:
+ *
+ * Starting from options
+ * start_addr, readwrite_len, spage, npages
+ * and using device memory size, compute
+ * start, end, first_page, num_pages
+ */
+ if (start_addr || readwrite_len) {
+ start = start_addr;
+
+ if (is_addr_in_flash(start))
+ end = stm->dev->fl_end;
+ else {
+ no_erase = 1;
+ if (is_addr_in_ram(start))
+ end = stm->dev->ram_end;
+ else
+ end = start + sizeof(uint32_t);
+ }
- if (rd) {
- fprintf(diag, "\n");
+ if (readwrite_len && (end > start + readwrite_len))
+ end = start + readwrite_len;
- if ((perr = parser->open(p_st, filename, 1)) != PARSER_ERR_OK) {
- fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr));
- if (perr == PARSER_ERR_SYSTEM) perror(filename);
+ first_page = flash_addr_to_page_floor(start);
+ if (!first_page && end == stm->dev->fl_end)
+ num_pages = 0xff; /* mass erase */
+ else
+ num_pages = flash_addr_to_page_ceil(end) - first_page;
+ } else if (!spage && !npages) {
+ start = stm->dev->fl_start;
+ end = stm->dev->fl_end;
+ first_page = 0;
+ num_pages = 0xff; /* mass erase */
+ } else {
+ first_page = spage;
+ start = flash_page_to_addr(first_page);
+ if (start > stm->dev->fl_end) {
+ fprintf(stderr, "Address range exceeds flash size.\n");
goto close;
}
- if (start_addr || readwrite_len) {
- start = start_addr;
- if (readwrite_len)
- end = start_addr + readwrite_len;
- else
+ if (npages) {
+ num_pages = npages;
+ end = flash_page_to_addr(first_page + num_pages);
+ if (end > stm->dev->fl_end)
end = stm->dev->fl_end;
} else {
- start = stm->dev->fl_start + (spage * stm->dev->fl_ps);
end = stm->dev->fl_end;
+ num_pages = flash_addr_to_page_ceil(end) - first_page;
}
- addr = start;
- if (start < stm->dev->fl_start || end > stm->dev->fl_end) {
- fprintf(stderr, "Specified start & length are invalid\n");
+ if (!first_page && end == stm->dev->fl_end)
+ num_pages = 0xff; /* mass erase */
+ }
+
+ if (rd) {
+ unsigned int max_len = port_opts.rx_frame_max;
+
+ fprintf(diag, "Memory read\n");
+
+ perr = parser->open(p_st, filename, 1);
+ if (perr != PARSER_ERR_OK) {
+ fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr));
+ if (perr == PARSER_ERR_SYSTEM)
+ perror(filename);
goto close;
}
fflush(diag);
+ addr = start;
while(addr < end) {
uint32_t left = end - addr;
- len = sizeof(buffer) > left ? left : sizeof(buffer);
- if (!stm32_read_memory(stm, addr, buffer, len)) {
+ len = max_len > left ? left : max_len;
+ s_err = stm32_read_memory(stm, addr, buffer, len);
+ if (s_err != STM32_ERR_OK) {
fprintf(stderr, "Failed to read memory at address 0x%08x, target write-protected?\n", addr);
goto close;
}
@@ -236,29 +315,21 @@ int main(int argc, char* argv[]) {
} else if (eraseOnly) {
ret = 0;
fprintf(stdout, "Erasing flash\n");
- if (start_addr || readwrite_len) {
- if ((start_addr % stm->dev->fl_ps) != 0
- || (readwrite_len % stm->dev->fl_ps) != 0) {
- fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n");
- ret = 1;
- goto close;
- }
- spage = (start_addr - stm->dev->fl_start) / stm->dev->fl_ps;
- if (readwrite_len)
- npages = readwrite_len / stm->dev->fl_ps;
- else
- npages = (stm->dev->fl_end - stm->dev->fl_start) / stm->dev->fl_ps;
- }
- if (!spage && !npages)
- npages = 0xff; /* mass erase */
+ if (num_pages != 0xff &&
+ (start != flash_page_to_addr(first_page)
+ || end != flash_page_to_addr(first_page + num_pages))) {
+ fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n");
+ ret = 1;
+ goto close;
+ }
- if (!stm32_erase_memory(stm, spage, npages)) {
+ s_err = stm32_erase_memory(stm, first_page, num_pages);
+ if (s_err != STM32_ERR_OK) {
fprintf(stderr, "Failed to erase memory\n");
ret = 1;
goto close;
}
-
} else if (wu) {
fprintf(diag, "Write-unprotecting flash\n");
/* the device automatically performs a reset after the sending the ACK */
@@ -267,49 +338,25 @@ int main(int argc, char* argv[]) {
fprintf(diag, "Done.\n");
} else if (wr) {
- fprintf(diag, "\n");
+ fprintf(diag, "Write to memory\n");
off_t offset = 0;
ssize_t r;
unsigned int size;
+ unsigned int max_wlen, max_rlen;
+
+ max_wlen = port_opts.tx_frame_max - 2; /* skip len and crc */
+ max_wlen &= ~3; /* 32 bit aligned */
+
+ max_rlen = port_opts.rx_frame_max;
+ max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen;
/* Assume data from stdin is whole device */
- if (filename[0] == '-')
- size = stm->dev->fl_end - stm->dev->fl_start;
+ if (filename[0] == '-' && filename[1] == '\0')
+ size = end - start;
else
size = parser->size(p_st);
- if (start_addr || readwrite_len) {
- start = start_addr;
- spage = (start_addr - stm->dev->fl_start) / stm->dev->fl_ps;
- if (readwrite_len) {
- end = start_addr + readwrite_len;
- npages = (end - stm->dev->fl_start + stm->dev->fl_ps - 1) / stm->dev->fl_ps - spage;
- } else {
- end = stm->dev->fl_end;
- if (spage)
- npages = (end - stm->dev->fl_start) / stm->dev->fl_ps - spage;
- else
- npages = 0xff; /* mass erase */
- }
- } else if (!spage && !npages) {
- start = stm->dev->fl_start;
- end = stm->dev->fl_end;
- npages = 0xff; /* mass erase */
- } else {
- start = stm->dev->fl_start + (spage * stm->dev->fl_ps);
- if (npages)
- end = start + npages * stm->dev->fl_ps;
- else
- end = stm->dev->fl_end;
- }
- addr = start;
-
- if (start < stm->dev->fl_start || end > stm->dev->fl_end) {
- fprintf(stderr, "Specified start & length are invalid\n");
- goto close;
- }
-
// TODO: It is possible to write to non-page boundaries, by reading out flash
// from partial pages and combining with the input data
// if ((start % stm->dev->fl_ps) != 0 || (end % stm->dev->fl_ps) != 0) {
@@ -319,15 +366,20 @@ int main(int argc, char* argv[]) {
// TODO: If writes are not page aligned, we should probably read out existing flash
// contents first, so it can be preserved and combined with new data
- if (!stm32_erase_memory(stm, spage, npages)) {
- fprintf(stderr, "Failed to erase memory\n");
- goto close;
+ if (!no_erase && num_pages) {
+ fprintf(diag, "Erasing memory\n");
+ s_err = stm32_erase_memory(stm, first_page, num_pages);
+ if (s_err != STM32_ERR_OK) {
+ fprintf(stderr, "Failed to erase memory\n");
+ goto close;
+ }
}
fflush(diag);
+ addr = start;
while(addr < end && offset < size) {
uint32_t left = end - addr;
- len = sizeof(buffer) > left ? left : sizeof(buffer);
+ len = max_wlen > left ? left : max_wlen;
len = len > size - offset ? size - offset : len;
if (parser->read(p_st, buffer, &len) != PARSER_ERR_OK)
@@ -343,16 +395,26 @@ int main(int argc, char* argv[]) {
}
again:
- if (!stm32_write_memory(stm, addr, buffer, len)) {
+ s_err = stm32_write_memory(stm, addr, buffer, len);
+ if (s_err != STM32_ERR_OK) {
fprintf(stderr, "Failed to write memory at address 0x%08x\n", addr);
goto close;
}
if (verify) {
uint8_t compare[len];
- if (!stm32_read_memory(stm, addr, compare, len)) {
- fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr);
- goto close;
+ unsigned int offset, rlen;
+
+ offset = 0;
+ while (offset < len) {
+ rlen = len - offset;
+ rlen = rlen < max_rlen ? rlen : max_rlen;
+ s_err = stm32_read_memory(stm, addr + offset, compare + offset, rlen);
+ if (s_err != STM32_ERR_OK) {
+ fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr + offset);
+ goto close;
+ }
+ offset += rlen;
}
for(r = 0; r < len; ++r)
@@ -388,6 +450,20 @@ int main(int argc, char* argv[]) {
fprintf(diag, "Done.\n");
ret = 0;
goto close;
+ } else if (crc) {
+ uint32_t crc_val = 0;
+
+ fprintf(diag, "CRC computation\n");
+
+ s_err = stm32_crc_wrapper(stm, start, end - start, &crc_val);
+ if (s_err != STM32_ERR_OK) {
+ fprintf(stderr, "Failed to read CRC\n");
+ goto close;
+ }
+ fprintf(diag, "CRC(0x%08x-0x%08x) = 0x%08x\n", start, end,
+ crc_val);
+ ret = 0;
+ goto close;
} else
ret = 0;
@@ -398,7 +474,7 @@ close:
fprintf(diag, "\nStarting execution at address 0x%08x... ", execute);
fflush(diag);
- if (stm32_go(stm, execute)) {
+ if (stm32_go(stm, execute) == STM32_ERR_OK) {
reset_flag = 0;
fprintf(diag, "done.\n");
} else
@@ -408,29 +484,38 @@ close:
if (stm && reset_flag) {
fprintf(diag, "\nResetting device... ");
fflush(diag);
- if (init_bl_exit(stm, serial, gpio_seq))
+ if (init_bl_exit(stm, port, gpio_seq))
fprintf(diag, "done.\n");
else fprintf(diag, "failed.\n");
}
if (p_st ) parser->close(p_st);
if (stm ) stm32_close (stm);
- if (serial) serial_close (serial);
+ if (port)
+ port->close(port);
fprintf(diag, "\n");
return ret;
}
-int parse_options(int argc, char *argv[]) {
+int parse_options(int argc, char *argv[])
+{
int c;
- while((c = getopt(argc, argv, "b:m:r:w:e:vn:g:jkfchuos:S:i:")) != -1) {
+ char *pLen;
+
+ while ((c = getopt(argc, argv, "a:b:m:r:w:e:vn:g:jkfcChuos:S:F:i:R")) != -1) {
switch(c) {
+ case 'a':
+ port_opts.bus_addr = strtoul(optarg, NULL, 0);
+ break;
+
case 'b':
- baudRate = serial_get_baud(strtoul(optarg, NULL, 0));
- if (baudRate == SERIAL_BAUD_INVALID) {
+ port_opts.baudRate = serial_get_baud(strtoul(optarg, NULL, 0));
+ if (port_opts.baudRate == SERIAL_BAUD_INVALID) {
+ serial_baud_t baudrate;
fprintf(stderr, "Invalid baud rate, valid options are:\n");
- for(baudRate = SERIAL_BAUD_1200; baudRate != SERIAL_BAUD_INVALID; ++baudRate)
- fprintf(stderr, " %d\n", serial_get_baud_int(baudRate));
+ for (baudrate = SERIAL_BAUD_1200; baudrate != SERIAL_BAUD_INVALID; ++baudrate)
+ fprintf(stderr, " %d\n", serial_get_baud_int(baudrate));
return 1;
}
break;
@@ -443,7 +528,7 @@ int parse_options(int argc, char *argv[]) {
fprintf(stderr, "Invalid serial mode\n");
return 1;
}
- serial_mode = optarg;
+ port_opts.serial_mode = optarg;
break;
case 'r':
@@ -469,6 +554,8 @@ int parse_options(int argc, char *argv[]) {
fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255");
return 1;
}
+ if (!npages)
+ no_erase = 1;
break;
case 'u':
wu = 1;
@@ -530,7 +617,6 @@ int parse_options(int argc, char *argv[]) {
fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n");
return 1;
} else {
- char *pLen;
start_addr = strtoul(optarg, &pLen, 0);
if (*pLen == ':') {
pLen++;
@@ -542,6 +628,36 @@ int parse_options(int argc, char *argv[]) {
}
}
break;
+ case 'F':
+ port_opts.rx_frame_max = strtoul(optarg, &pLen, 0);
+ if (*pLen == ':') {
+ pLen++;
+ port_opts.tx_frame_max = strtoul(pLen, NULL, 0);
+ }
+ if (port_opts.rx_frame_max < 0
+ || port_opts.tx_frame_max < 0) {
+ fprintf(stderr, "ERROR: Invalid negative value for option -F\n");
+ return 1;
+ }
+ if (port_opts.rx_frame_max == 0)
+ port_opts.rx_frame_max = STM32_MAX_RX_FRAME;
+ if (port_opts.tx_frame_max == 0)
+ port_opts.tx_frame_max = STM32_MAX_TX_FRAME;
+ if (port_opts.rx_frame_max < 20
+ || port_opts.tx_frame_max < 5) {
+ fprintf(stderr, "ERROR: current code cannot work with small frames.\n");
+ fprintf(stderr, "min(RX) = 20, min(TX) = 5\n");
+ return 1;
+ }
+ if (port_opts.rx_frame_max > STM32_MAX_RX_FRAME) {
+ fprintf(stderr, "WARNING: Ignore RX length in option -F\n");
+ port_opts.rx_frame_max = STM32_MAX_RX_FRAME;
+ }
+ if (port_opts.tx_frame_max > STM32_MAX_TX_FRAME) {
+ fprintf(stderr, "WARNING: Ignore TX length in option -F\n");
+ port_opts.tx_frame_max = STM32_MAX_TX_FRAME;
+ }
+ break;
case 'f':
force_binary = 1;
break;
@@ -557,19 +673,27 @@ int parse_options(int argc, char *argv[]) {
case 'i':
gpio_seq = optarg;
break;
+
+ case 'R':
+ reset_flag = 1;
+ break;
+
+ case 'C':
+ crc = 1;
+ break;
}
}
for (c = optind; c < argc; ++c) {
- if (device) {
+ if (port_opts.device) {
fprintf(stderr, "ERROR: Invalid parameter specified\n");
show_help(argv[0]);
return 1;
}
- device = argv[c];
+ port_opts.device = argv[c];
}
- if (device == NULL) {
+ if (port_opts.device == NULL) {
fprintf(stderr, "ERROR: Device not specified\n");
show_help(argv[0]);
return 1;
@@ -586,11 +710,13 @@ int parse_options(int argc, char *argv[]) {
void show_help(char *name) {
fprintf(stderr,
- "Usage: %s [-bvngfhc] [-[rw] filename] /dev/ttyS0\n"
+ "Usage: %s [-bvngfhc] [-[rw] filename] [tty_device | i2c_device]\n"
+ " -a bus_address Bus address (e.g. for I2C port)\n"
" -b rate Baud rate (default 57600)\n"
" -m mode Serial port mode (default 8e1)\n"
" -r filename Read flash to file (or - stdout)\n"
" -w filename Write flash from file (or - stdout)\n"
+ " -C Compute CRC of flash content\n"
" -u Disable the flash write-protection\n"
" -j Enable the flash read-protection\n"
" -k Disable the flash read-protection\n"
@@ -601,6 +727,7 @@ void show_help(char *name) {
" -g address Start execution at specified address (0 = flash start)\n"
" -S address[:length] Specify start address and optionally length for\n"
" read/write/erase operations\n"
+ " -F RX_length[:TX_length] Specify the max length of RX and TX frame\n"
" -s start_page Flash at specified page (0 = flash start)\n"
" -f Force binary parser\n"
" -h Show this help\n"
@@ -610,10 +737,13 @@ void show_help(char *name) {
" -i GPIO_string GPIO sequence to enter/exit bootloader mode\n"
" GPIO_string=[entry_seq][:[exit_seq]]\n"
" sequence=[-]n[,sequence]\n"
+ " -R Reset device at exit.\n"
"\n"
"Examples:\n"
" Get device information:\n"
" %s /dev/ttyS0\n"
+ " or:\n"
+ " %s /dev/i2c-0\n"
"\n"
" Write with verify and then start execution:\n"
" %s -w filename -v -g 0x0 /dev/ttyS0\n"
@@ -637,6 +767,7 @@ void show_help(char *name) {
name,
name,
name,
+ name,
name
);
}
diff --git a/parsers/Android.mk b/parsers/Android.mk
new file mode 100644
index 0000000..afec18c
--- /dev/null
+++ b/parsers/Android.mk
@@ -0,0 +1,6 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libparsers
+LOCAL_SRC_FILES := binary.c hex.c
+include $(BUILD_STATIC_LIBRARY)
diff --git a/parsers/hex.c b/parsers/hex.c
index 76a8d64..3baf856 100644
--- a/parsers/hex.c
+++ b/parsers/hex.c
@@ -64,7 +64,7 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) {
char buffer[9];
unsigned int reclen, address, type;
- uint8_t *record;
+ uint8_t *record = NULL;
/* get the reclen, address, and type */
buffer[8] = 0;
@@ -121,7 +121,11 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) {
switch(type) {
case 0:
- record[i] = c;
+ if (record != NULL) {
+ record[i] = c;
+ } else {
+ return PARSER_ERR_INVALID_FILE;
+ }
break;
case 2:
diff --git a/port.c b/port.c
new file mode 100644
index 0000000..08e58cc
--- /dev/null
+++ b/port.c
@@ -0,0 +1,59 @@
+/*
+ stm32flash - Open Source ST STM32 flash program for *nix
+ Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "serial.h"
+#include "port.h"
+
+
+extern struct port_interface port_serial;
+extern struct port_interface port_i2c;
+
+static struct port_interface *ports[] = {
+ &port_serial,
+ &port_i2c,
+ NULL,
+};
+
+
+port_err_t port_open(struct port_options *ops, struct port_interface **outport)
+{
+ int ret;
+ static struct port_interface **port;
+
+ for (port = ports; *port; port++) {
+ ret = (*port)->open(*port, ops);
+ if (ret == PORT_ERR_NODEV)
+ continue;
+ if (ret == PORT_ERR_OK)
+ break;
+ fprintf(stderr, "Error probing interface \"%s\"\n",
+ (*port)->name);
+ }
+ if (*port == NULL) {
+ fprintf(stderr, "Cannot handle device \"%s\"\n",
+ ops->device);
+ return PORT_ERR_UNKNOWN;
+ }
+
+ *outport = *port;
+ return PORT_ERR_OK;
+}
diff --git a/port.h b/port.h
new file mode 100644
index 0000000..290f034
--- /dev/null
+++ b/port.h
@@ -0,0 +1,75 @@
+/*
+ stm32flash - Open Source ST STM32 flash program for *nix
+ Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+
+#ifndef _H_PORT
+#define _H_PORT
+
+typedef enum {
+ PORT_ERR_OK = 0,
+ PORT_ERR_NODEV, /* No such device */
+ PORT_ERR_TIMEDOUT, /* Operation timed out */
+ PORT_ERR_UNKNOWN,
+} port_err_t;
+
+/* flags */
+#define PORT_BYTE (1 << 0) /* byte (not frame) oriented */
+#define PORT_GVR_ETX (1 << 1) /* cmd GVR returns protection status */
+#define PORT_CMD_INIT (1 << 2) /* use INIT cmd to autodetect speed */
+#define PORT_RETRY (1 << 3) /* allowed read() retry after timeout */
+#define PORT_STRETCH_W (1 << 4) /* warning for no-stretching commands */
+
+/* all options and flags used to open and configure an interface */
+struct port_options {
+ const char *device;
+ serial_baud_t baudRate;
+ const char *serial_mode;
+ int bus_addr;
+ int rx_frame_max;
+ int tx_frame_max;
+};
+
+/*
+ * Specify the length of reply for command GET
+ * This is helpful for frame-oriented protocols, e.g. i2c, to avoid time
+ * consuming try-fail-timeout-retry operation.
+ * On byte-oriented protocols, i.e. UART, this information would be skipped
+ * after read the first byte, so not needed.
+ */
+struct varlen_cmd {
+ uint8_t version;
+ uint8_t length;
+};
+
+struct port_interface {
+ const char *name;
+ unsigned flags;
+ port_err_t (*open)(struct port_interface *port, struct port_options *ops);
+ port_err_t (*close)(struct port_interface *port);
+ port_err_t (*read)(struct port_interface *port, void *buf, size_t nbyte);
+ port_err_t (*write)(struct port_interface *port, void *buf, size_t nbyte);
+ port_err_t (*gpio)(struct port_interface *port, serial_gpio_t n, int level);
+ const char *(*get_cfg_str)(struct port_interface *port);
+ struct varlen_cmd *cmd_get_reply;
+ void *private;
+};
+
+port_err_t port_open(struct port_options *ops, struct port_interface **outport);
+
+#endif
diff --git a/protocol.txt b/protocol.txt
new file mode 100644
index 0000000..0391099
--- /dev/null
+++ b/protocol.txt
@@ -0,0 +1,19 @@
+The communication protocol used by ST bootloader is documented in following ST
+application notes, depending on communication port.
+
+In current version of stm32flash are supported only UART and I2C ports.
+
+* AN3154: CAN protocol used in the STM32 bootloader
+ http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf
+
+* AN3155: USART protocol used in the STM32(TM) bootloader
+ http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf
+
+* AN4221: I2C protocol used in the STM32 bootloader
+ http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf
+
+* AN4286: SPI protocol used in the STM32 bootloader
+ http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf
+
+Boot mode selection for STM32 is documented in ST application note AN2606, available in ST website:
+ http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf
diff --git a/serial.h b/serial.h
index e1f9b09..227ba16 100644
--- a/serial.h
+++ b/serial.h
@@ -21,8 +21,6 @@
#ifndef _SERIAL_H
#define _SERIAL_H
-#include <stdint.h>
-
typedef struct serial serial_t;
typedef enum {
@@ -74,40 +72,19 @@ typedef enum {
} serial_stopbit_t;
typedef enum {
- SERIAL_ERR_OK = 0,
-
- SERIAL_ERR_SYSTEM,
- SERIAL_ERR_UNKNOWN,
- SERIAL_ERR_INVALID_BAUD,
- SERIAL_ERR_INVALID_BITS,
- SERIAL_ERR_INVALID_PARITY,
- SERIAL_ERR_INVALID_STOPBIT,
- SERIAL_ERR_NODATA
-} serial_err_t;
-
-typedef enum {
GPIO_RTS = 1,
GPIO_DTR,
GPIO_BRK,
} serial_gpio_t;
-serial_t* serial_open (const char *device);
-void serial_close(serial_t *h);
-void serial_flush(const serial_t *h);
-serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bits_t bits, const serial_parity_t parity, const serial_stopbit_t stopbit);
-serial_err_t serial_write(const serial_t *h, const void *buffer, unsigned int len);
-serial_err_t serial_read (const serial_t *h, void *buffer, unsigned int len);
-const char* serial_get_setup_str(const serial_t *h);
-serial_err_t serial_gpio (const serial_t *h, serial_gpio_t n, int level);
-
/* common helper functions */
-serial_baud_t serial_get_baud (const unsigned int baud);
-unsigned int serial_get_baud_int (const serial_baud_t baud);
-serial_bits_t serial_get_bits (const char *mode);
-unsigned int serial_get_bits_int (const serial_bits_t bits);
-serial_parity_t serial_get_parity (const char *mode);
-char serial_get_parity_str (const serial_parity_t parity);
-serial_stopbit_t serial_get_stopbit (const char *mode);
+serial_baud_t serial_get_baud(const unsigned int baud);
+unsigned int serial_get_baud_int(const serial_baud_t baud);
+serial_bits_t serial_get_bits(const char *mode);
+unsigned int serial_get_bits_int(const serial_bits_t bits);
+serial_parity_t serial_get_parity(const char *mode);
+char serial_get_parity_str(const serial_parity_t parity);
+serial_stopbit_t serial_get_stopbit(const char *mode);
unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit);
#endif
diff --git a/serial_posix.c b/serial_posix.c
index 5a23991..46b8a94 100644
--- a/serial_posix.c
+++ b/serial_posix.c
@@ -17,30 +17,27 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
-#include <stdlib.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <termios.h>
+#include <stdint.h>
#include <stdio.h>
-#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
#include <sys/ioctl.h>
#include "serial.h"
+#include "port.h"
struct serial {
- int fd;
- struct termios oldtio;
- struct termios newtio;
-
- char configured;
- serial_baud_t baud;
- serial_bits_t bits;
- serial_parity_t parity;
- serial_stopbit_t stopbit;
+ int fd;
+ struct termios oldtio;
+ struct termios newtio;
+ char setup_str[11];
};
-serial_t* serial_open(const char *device) {
+static serial_t *serial_open(const char *device)
+{
serial_t *h = calloc(sizeof(serial_t), 1);
h->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
@@ -56,50 +53,52 @@ serial_t* serial_open(const char *device) {
return h;
}
-void serial_close(serial_t *h) {
- assert(h && h->fd > -1);
+static void serial_flush(const serial_t *h)
+{
+ tcflush(h->fd, TCIFLUSH);
+}
+static void serial_close(serial_t *h)
+{
serial_flush(h);
tcsetattr(h->fd, TCSANOW, &h->oldtio);
close(h->fd);
free(h);
}
-void serial_flush(const serial_t *h) {
- assert(h && h->fd > -1);
- tcflush(h->fd, TCIFLUSH);
-}
+static port_err_t serial_setup(serial_t *h, const serial_baud_t baud,
+ const serial_bits_t bits,
+ const serial_parity_t parity,
+ const serial_stopbit_t stopbit)
+{
+ speed_t port_baud;
+ tcflag_t port_bits;
+ tcflag_t port_parity;
+ tcflag_t port_stop;
+ struct termios settings;
-serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bits_t bits, const serial_parity_t parity, const serial_stopbit_t stopbit) {
- assert(h && h->fd > -1);
-
- speed_t port_baud;
- tcflag_t port_bits;
- tcflag_t port_parity;
- tcflag_t port_stop;
-
- switch(baud) {
- case SERIAL_BAUD_1200 : port_baud = B1200 ; break;
- case SERIAL_BAUD_1800 : port_baud = B1800 ; break;
- case SERIAL_BAUD_2400 : port_baud = B2400 ; break;
- case SERIAL_BAUD_4800 : port_baud = B4800 ; break;
- case SERIAL_BAUD_9600 : port_baud = B9600 ; break;
- case SERIAL_BAUD_19200 : port_baud = B19200 ; break;
- case SERIAL_BAUD_38400 : port_baud = B38400 ; break;
- case SERIAL_BAUD_57600 : port_baud = B57600 ; break;
- case SERIAL_BAUD_115200: port_baud = B115200; break;
- case SERIAL_BAUD_230400: port_baud = B230400; break;
+ switch (baud) {
+ case SERIAL_BAUD_1200: port_baud = B1200; break;
+ case SERIAL_BAUD_1800: port_baud = B1800; break;
+ case SERIAL_BAUD_2400: port_baud = B2400; break;
+ case SERIAL_BAUD_4800: port_baud = B4800; break;
+ case SERIAL_BAUD_9600: port_baud = B9600; break;
+ case SERIAL_BAUD_19200: port_baud = B19200; break;
+ case SERIAL_BAUD_38400: port_baud = B38400; break;
+ case SERIAL_BAUD_57600: port_baud = B57600; break;
+ case SERIAL_BAUD_115200: port_baud = B115200; break;
+ case SERIAL_BAUD_230400: port_baud = B230400; break;
#ifdef B460800
- case SERIAL_BAUD_460800: port_baud = B460800; break;
+ case SERIAL_BAUD_460800: port_baud = B460800; break;
#endif /* B460800 */
#ifdef B921600
- case SERIAL_BAUD_921600: port_baud = B921600; break;
+ case SERIAL_BAUD_921600: port_baud = B921600; break;
#endif /* B921600 */
#ifdef B500000
- case SERIAL_BAUD_500000: port_baud = B500000; break;
+ case SERIAL_BAUD_500000: port_baud = B500000; break;
#endif /* B500000 */
#ifdef B576000
- case SERIAL_BAUD_576000: port_baud = B576000; break;
+ case SERIAL_BAUD_576000: port_baud = B576000; break;
#endif /* B576000 */
#ifdef B1000000
case SERIAL_BAUD_1000000: port_baud = B1000000; break;
@@ -113,45 +112,36 @@ serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bi
case SERIAL_BAUD_INVALID:
default:
- return SERIAL_ERR_INVALID_BAUD;
+ return PORT_ERR_UNKNOWN;
}
- switch(bits) {
+ switch (bits) {
case SERIAL_BITS_5: port_bits = CS5; break;
case SERIAL_BITS_6: port_bits = CS6; break;
case SERIAL_BITS_7: port_bits = CS7; break;
case SERIAL_BITS_8: port_bits = CS8; break;
default:
- return SERIAL_ERR_INVALID_BITS;
+ return PORT_ERR_UNKNOWN;
}
- switch(parity) {
- case SERIAL_PARITY_NONE: port_parity = 0; break;
- case SERIAL_PARITY_EVEN: port_parity = INPCK | PARENB; break;
- case SERIAL_PARITY_ODD : port_parity = INPCK | PARENB | PARODD; break;
+ switch (parity) {
+ case SERIAL_PARITY_NONE: port_parity = 0; break;
+ case SERIAL_PARITY_EVEN: port_parity = INPCK | PARENB; break;
+ case SERIAL_PARITY_ODD: port_parity = INPCK | PARENB | PARODD; break;
default:
- return SERIAL_ERR_INVALID_PARITY;
+ return PORT_ERR_UNKNOWN;
}
- switch(stopbit) {
+ switch (stopbit) {
case SERIAL_STOPBIT_1: port_stop = 0; break;
case SERIAL_STOPBIT_2: port_stop = CSTOPB; break;
default:
- return SERIAL_ERR_INVALID_STOPBIT;
+ return PORT_ERR_UNKNOWN;
}
- /* if the port is already configured, no need to do anything */
- if (
- h->configured &&
- h->baud == baud &&
- h->bits == bits &&
- h->parity == parity &&
- h->stopbit == stopbit
- ) return SERIAL_ERR_OK;
-
/* reset the settings */
#ifndef __sun /* Used by GNU and BSD. Ignore __SVR4 in test. */
cfmakeraw(&h->newtio);
@@ -183,111 +173,182 @@ serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bi
CLOCAL |
CREAD;
- h->newtio.c_cc[VMIN ] = 0;
- h->newtio.c_cc[VTIME] = 180;
+ h->newtio.c_cc[VMIN] = 0;
+ h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */
/* set the settings */
serial_flush(h);
if (tcsetattr(h->fd, TCSANOW, &h->newtio) != 0)
- return SERIAL_ERR_SYSTEM;
+ return PORT_ERR_UNKNOWN;
/* confirm they were set */
- struct termios settings;
tcgetattr(h->fd, &settings);
- if (
- settings.c_iflag != h->newtio.c_iflag ||
- settings.c_oflag != h->newtio.c_oflag ||
- settings.c_cflag != h->newtio.c_cflag ||
- settings.c_lflag != h->newtio.c_lflag
- ) return SERIAL_ERR_UNKNOWN;
-
- h->configured = 1;
- h->baud = baud;
- h->bits = bits;
- h->parity = parity;
- h->stopbit = stopbit;
- return SERIAL_ERR_OK;
+ if (settings.c_iflag != h->newtio.c_iflag ||
+ settings.c_oflag != h->newtio.c_oflag ||
+ settings.c_cflag != h->newtio.c_cflag ||
+ settings.c_lflag != h->newtio.c_lflag)
+ return PORT_ERR_UNKNOWN;
+
+ snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d",
+ serial_get_baud_int(baud),
+ serial_get_bits_int(bits),
+ serial_get_parity_str(parity),
+ serial_get_stopbit_int(stopbit));
+ return PORT_ERR_OK;
}
-serial_err_t serial_write(const serial_t *h, const void *buffer, unsigned int len) {
- assert(h && h->fd > -1 && h->configured);
+static port_err_t serial_posix_open(struct port_interface *port,
+ struct port_options *ops)
+{
+ serial_t *h;
+
+ /* 1. check device name match */
+ if (strncmp(ops->device, "/dev/tty", strlen("/dev/tty")))
+ return PORT_ERR_NODEV;
+
+ /* 2. check options */
+ if (ops->baudRate == SERIAL_BAUD_INVALID)
+ return PORT_ERR_UNKNOWN;
+ if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID)
+ return PORT_ERR_UNKNOWN;
+ if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID)
+ return PORT_ERR_UNKNOWN;
+ if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID)
+ return PORT_ERR_UNKNOWN;
+
+ /* 3. open it */
+ h = serial_open(ops->device);
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+
+ /* 4. set options */
+ if (serial_setup(h, ops->baudRate,
+ serial_get_bits(ops->serial_mode),
+ serial_get_parity(ops->serial_mode),
+ serial_get_stopbit(ops->serial_mode)
+ ) != PORT_ERR_OK) {
+ serial_close(h);
+ return PORT_ERR_UNKNOWN;
+ }
- ssize_t r;
- const uint8_t *pos = (const uint8_t*)buffer;
+ port->private = h;
+ return PORT_ERR_OK;
+}
- while(len > 0) {
- r = write(h->fd, pos, len);
- if (r < 1) return SERIAL_ERR_SYSTEM;
+static port_err_t serial_posix_close(struct port_interface *port)
+{
+ serial_t *h;
- len -= r;
- pos += r;
- }
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
- return SERIAL_ERR_OK;
+ serial_close(h);
+ port->private = NULL;
+ return PORT_ERR_OK;
}
-serial_err_t serial_read(const serial_t *h, void *buffer, unsigned int len) {
- assert(h && h->fd > -1 && h->configured);
-
+static port_err_t serial_posix_read(struct port_interface *port, void *buf,
+ size_t nbyte)
+{
+ serial_t *h;
ssize_t r;
- uint8_t *pos = (uint8_t*)buffer;
+ uint8_t *pos = (uint8_t *)buf;
+
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
- while(len > 0) {
- r = read(h->fd, pos, len);
- if (r == 0) return SERIAL_ERR_NODATA;
- else if (r < 0) return SERIAL_ERR_SYSTEM;
+ while (nbyte) {
+ r = read(h->fd, pos, nbyte);
+ if (r == 0)
+ return PORT_ERR_TIMEDOUT;
+ if (r < 0)
+ return PORT_ERR_UNKNOWN;
- len -= r;
+ nbyte -= r;
pos += r;
}
-
- return SERIAL_ERR_OK;
+ return PORT_ERR_OK;
}
-const char* serial_get_setup_str(const serial_t *h) {
- static char str[11];
- if (!h->configured)
- snprintf(str, sizeof(str), "INVALID");
- else
- snprintf(str, sizeof(str), "%u %d%c%d",
- serial_get_baud_int (h->baud ),
- serial_get_bits_int (h->bits ),
- serial_get_parity_str (h->parity ),
- serial_get_stopbit_int(h->stopbit)
- );
-
- return str;
+static port_err_t serial_posix_write(struct port_interface *port, void *buf,
+ size_t nbyte)
+{
+ serial_t *h;
+ ssize_t r;
+ const uint8_t *pos = (const uint8_t *)buf;
+
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+
+ while (nbyte) {
+ r = write(h->fd, pos, nbyte);
+ if (r < 1)
+ return PORT_ERR_UNKNOWN;
+
+ nbyte -= r;
+ pos += r;
+ }
+ return PORT_ERR_OK;
}
-serial_err_t serial_gpio (const serial_t *h, serial_gpio_t n, int level) {
+static port_err_t serial_posix_gpio(struct port_interface *port,
+ serial_gpio_t n, int level)
+{
+ serial_t *h;
int bit, lines;
- switch(n) {
- case GPIO_RTS:
- bit = TIOCM_RTS;
- break;
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
- case GPIO_DTR:
- bit = TIOCM_DTR;
- break;
+ switch (n) {
+ case GPIO_RTS:
+ bit = TIOCM_RTS;
+ break;
- case GPIO_BRK:
- if (level == 0)
- return SERIAL_ERR_OK;
- if (tcsendbreak(h->fd, 1))
- return SERIAL_ERR_SYSTEM;
- return SERIAL_ERR_OK;
+ case GPIO_DTR:
+ bit = TIOCM_DTR;
+ break;
- default:
- return SERIAL_ERR_NODATA;
+ case GPIO_BRK:
+ if (level == 0)
+ return PORT_ERR_OK;
+ if (tcsendbreak(h->fd, 1))
+ return PORT_ERR_UNKNOWN;
+ return PORT_ERR_OK;
+
+ default:
+ return PORT_ERR_UNKNOWN;
}
/* handle RTS/DTR */
if (ioctl(h->fd, TIOCMGET, &lines))
- return SERIAL_ERR_SYSTEM;
+ return PORT_ERR_UNKNOWN;
lines = level ? lines | bit : lines & ~bit;
if (ioctl(h->fd, TIOCMSET, &lines))
- return SERIAL_ERR_SYSTEM;
+ return PORT_ERR_UNKNOWN;
+
+ return PORT_ERR_OK;
+}
+
+static const char *serial_posix_get_cfg_str(struct port_interface *port)
+{
+ serial_t *h;
- return SERIAL_ERR_OK;
+ h = (serial_t *)port->private;
+ return h ? h->setup_str : "INVALID";
}
+
+struct port_interface port_serial = {
+ .name = "serial_posix",
+ .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY,
+ .open = serial_posix_open,
+ .close = serial_posix_close,
+ .read = serial_posix_read,
+ .write = serial_posix_write,
+ .gpio = serial_posix_gpio,
+ .get_cfg_str = serial_posix_get_cfg_str,
+};
diff --git a/serial_w32.c b/serial_w32.c
index d5683ae..ceac3a3 100644
--- a/serial_w32.c
+++ b/serial_w32.c
@@ -18,55 +18,52 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
-#include <stdlib.h>
#include <fcntl.h>
-#include <unistd.h>
+#include <stdint.h>
#include <stdio.h>
-#include <assert.h>
-
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <windows.h>
+#include <ctype.h>
#include "serial.h"
+#include "port.h"
struct serial {
HANDLE fd;
DCB oldtio;
DCB newtio;
-
- char configured;
- serial_baud_t baud;
- serial_bits_t bits;
- serial_parity_t parity;
- serial_stopbit_t stopbit;
+ char setup_str[11];
};
-serial_t* serial_open(const char *device)
+static serial_t *serial_open(const char *device)
{
serial_t *h = calloc(sizeof(serial_t), 1);
+ char *devName;
- COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 3000, 0, 0};
+ /* timeout in ms */
+ COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 500, 0, 0};
/* Fix the device name if required */
- char *devName;
if (strlen(device) > 4 && device[0] != '\\') {
devName = calloc(1, strlen(device) + 5);
sprintf(devName, "\\\\.\\%s", device);
} else {
- devName = device;
+ devName = (char *)device;
}
/* Create file handle for port */
- h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE,
- 0, /* Exclusive access */
- NULL, /* No security */
- OPEN_EXISTING,
- 0, //FILE_FLAG_OVERLAPPED,
- NULL);
+ h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE,
+ 0, /* Exclusive access */
+ NULL, /* No security */
+ OPEN_EXISTING,
+ 0, /* No overlap */
+ NULL);
if (devName != device)
free(devName);
-
+
if (h->fd == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND)
fprintf(stderr, "File not found: %s\n", device);
@@ -82,99 +79,85 @@ serial_t* serial_open(const char *device)
GetCommState(h->fd, &h->oldtio); /* Retrieve port parameters */
GetCommState(h->fd, &h->newtio); /* Retrieve port parameters */
- //PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
+ /* PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); */
return h;
}
-void serial_close(serial_t *h)
+static void serial_flush(const serial_t *h)
{
- assert(h && h->fd != INVALID_HANDLE_VALUE);
+ /* We shouldn't need to flush in non-overlapping (blocking) mode */
+ /* tcflush(h->fd, TCIFLUSH); */
+}
+static void serial_close(serial_t *h)
+{
serial_flush(h);
SetCommState(h->fd, &h->oldtio);
CloseHandle(h->fd);
free(h);
}
-void serial_flush(const serial_t *h)
-{
- assert(h && (h->fd != INVALID_HANDLE_VALUE));
- /* We shouldn't need to flush in non-overlapping (blocking) mode */
- //tcflush(h->fd, TCIFLUSH);
-}
-
-serial_err_t serial_setup(serial_t *h,
- const serial_baud_t baud,
- const serial_bits_t bits,
- const serial_parity_t parity,
- const serial_stopbit_t stopbit)
+static port_err_t serial_setup(serial_t *h,
+ const serial_baud_t baud,
+ const serial_bits_t bits,
+ const serial_parity_t parity,
+ const serial_stopbit_t stopbit)
{
- assert(h && h->fd != INVALID_HANDLE_VALUE);
-
- switch(baud) {
- case SERIAL_BAUD_1200 : h->newtio.BaudRate = CBR_1200 ; break;
- //case SERIAL_BAUD_1800 : h->newtio.BaudRate = CBR_1800 ; break;
- case SERIAL_BAUD_2400 : h->newtio.BaudRate = CBR_2400 ; break;
- case SERIAL_BAUD_4800 : h->newtio.BaudRate = CBR_4800 ; break;
- case SERIAL_BAUD_9600 : h->newtio.BaudRate = CBR_9600 ; break;
- case SERIAL_BAUD_19200 : h->newtio.BaudRate = CBR_19200 ; break;
- case SERIAL_BAUD_38400 : h->newtio.BaudRate = CBR_38400 ; break;
- case SERIAL_BAUD_57600 : h->newtio.BaudRate = CBR_57600 ; break;
- case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break;
- case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break;
- case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break;
+ switch (baud) {
+ case SERIAL_BAUD_1200: h->newtio.BaudRate = CBR_1200; break;
+ /* case SERIAL_BAUD_1800: h->newtio.BaudRate = CBR_1800; break; */
+ case SERIAL_BAUD_2400: h->newtio.BaudRate = CBR_2400; break;
+ case SERIAL_BAUD_4800: h->newtio.BaudRate = CBR_4800; break;
+ case SERIAL_BAUD_9600: h->newtio.BaudRate = CBR_9600; break;
+ case SERIAL_BAUD_19200: h->newtio.BaudRate = CBR_19200; break;
+ case SERIAL_BAUD_38400: h->newtio.BaudRate = CBR_38400; break;
+ case SERIAL_BAUD_57600: h->newtio.BaudRate = CBR_57600; break;
+ case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break;
+ case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break;
+ case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break;
/* These are not defined in WinBase.h and might work or not */
- case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break;
- case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break;
- case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break;
- case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break;
- case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break;
+ case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break;
+ case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break;
+ case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break;
+ case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break;
+ case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break;
case SERIAL_BAUD_1000000: h->newtio.BaudRate = 1000000; break;
case SERIAL_BAUD_1500000: h->newtio.BaudRate = 1500000; break;
case SERIAL_BAUD_2000000: h->newtio.BaudRate = 2000000; break;
-
case SERIAL_BAUD_INVALID:
+
default:
- return SERIAL_ERR_INVALID_BAUD;
+ return PORT_ERR_UNKNOWN;
}
- switch(bits) {
+ switch (bits) {
case SERIAL_BITS_5: h->newtio.ByteSize = 5; break;
case SERIAL_BITS_6: h->newtio.ByteSize = 6; break;
case SERIAL_BITS_7: h->newtio.ByteSize = 7; break;
case SERIAL_BITS_8: h->newtio.ByteSize = 8; break;
default:
- return SERIAL_ERR_INVALID_BITS;
+ return PORT_ERR_UNKNOWN;
}
- switch(parity) {
+ switch (parity) {
case SERIAL_PARITY_NONE: h->newtio.Parity = NOPARITY; break;
case SERIAL_PARITY_EVEN: h->newtio.Parity = EVENPARITY; break;
- case SERIAL_PARITY_ODD : h->newtio.Parity = ODDPARITY; break;
+ case SERIAL_PARITY_ODD: h->newtio.Parity = ODDPARITY; break;
default:
- return SERIAL_ERR_INVALID_PARITY;
+ return PORT_ERR_UNKNOWN;
}
- switch(stopbit) {
+ switch (stopbit) {
case SERIAL_STOPBIT_1: h->newtio.StopBits = ONESTOPBIT; break;
case SERIAL_STOPBIT_2: h->newtio.StopBits = TWOSTOPBITS; break;
default:
- return SERIAL_ERR_INVALID_STOPBIT;
+ return PORT_ERR_UNKNOWN;
}
- /* if the port is already configured, no need to do anything */
- if (
- h->configured &&
- h->baud == baud &&
- h->bits == bits &&
- h->parity == parity &&
- h->stopbit == stopbit
- ) return SERIAL_ERR_OK;
-
/* reset the settings */
h->newtio.fOutxCtsFlow = FALSE;
h->newtio.fOutxDsrFlow = FALSE;
@@ -186,99 +169,173 @@ serial_err_t serial_setup(serial_t *h,
/* set the settings */
serial_flush(h);
if (!SetCommState(h->fd, &h->newtio))
- return SERIAL_ERR_SYSTEM;
-
- h->configured = 1;
- h->baud = baud;
- h->bits = bits;
- h->parity = parity;
- h->stopbit = stopbit;
- return SERIAL_ERR_OK;
+ return PORT_ERR_UNKNOWN;
+
+ snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d",
+ serial_get_baud_int(baud),
+ serial_get_bits_int(bits),
+ serial_get_parity_str(parity),
+ serial_get_stopbit_int(stopbit)
+ );
+ return PORT_ERR_OK;
}
-serial_err_t serial_write(const serial_t *h, const void *buffer, unsigned int len)
+static port_err_t serial_w32_open(struct port_interface *port,
+ struct port_options *ops)
{
- assert(h && (h->fd != INVALID_HANDLE_VALUE) && h->configured);
+ serial_t *h;
+
+ /* 1. check device name match */
+ if (!(strlen(ops->device) == 4
+ && !strncmp(ops->device, "COM", 3) && isdigit(ops->device[3]))
+ && !(!strncmp(ops->device, "\\\\.\\COM", strlen("\\\\.\\COM"))
+ && isdigit(ops->device[strlen("\\\\.\\COM")])))
+ return PORT_ERR_NODEV;
+
+ /* 2. check options */
+ if (ops->baudRate == SERIAL_BAUD_INVALID)
+ return PORT_ERR_UNKNOWN;
+ if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID)
+ return PORT_ERR_UNKNOWN;
+ if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID)
+ return PORT_ERR_UNKNOWN;
+ if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID)
+ return PORT_ERR_UNKNOWN;
+
+ /* 3. open it */
+ h = serial_open(ops->device);
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+
+ /* 4. set options */
+ if (serial_setup(h, ops->baudRate,
+ serial_get_bits(ops->serial_mode),
+ serial_get_parity(ops->serial_mode),
+ serial_get_stopbit(ops->serial_mode)
+ ) != PORT_ERR_OK) {
+ serial_close(h);
+ return PORT_ERR_UNKNOWN;
+ }
- DWORD r;
- uint8_t *pos = (uint8_t*)buffer;
+ port->private = h;
+ return PORT_ERR_OK;
+}
- while(len > 0) {
- if(!WriteFile(h->fd, pos, len, &r, NULL))
- return SERIAL_ERR_SYSTEM;
- if (r < 1) return SERIAL_ERR_SYSTEM;
+static port_err_t serial_w32_close(struct port_interface *port)
+{
+ serial_t *h;
- len -= r;
- pos += r;
- }
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
- return SERIAL_ERR_OK;
+ serial_close(h);
+ port->private = NULL;
+ return PORT_ERR_OK;
}
-serial_err_t serial_read(const serial_t *h, void *buffer, unsigned int len)
+static port_err_t serial_w32_read(struct port_interface *port, void *buf,
+ size_t nbyte)
{
- assert(h && (h->fd != INVALID_HANDLE_VALUE) && h->configured);
-
+ serial_t *h;
DWORD r;
- uint8_t *pos = (uint8_t*)buffer;
+ uint8_t *pos = (uint8_t *)buf;
+
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
- while(len > 0) {
- ReadFile(h->fd, pos, len, &r, NULL);
- if (r == 0) return SERIAL_ERR_NODATA;
- else if (r < 0) return SERIAL_ERR_SYSTEM;
+ while (nbyte) {
+ ReadFile(h->fd, pos, nbyte, &r, NULL);
+ if (r == 0)
+ return PORT_ERR_TIMEDOUT;
+ if (r < 0)
+ return PORT_ERR_UNKNOWN;
- len -= r;
+ nbyte -= r;
pos += r;
}
-
- return SERIAL_ERR_OK;
+ return PORT_ERR_OK;
}
-const char* serial_get_setup_str(const serial_t *h)
+static port_err_t serial_w32_write(struct port_interface *port, void *buf,
+ size_t nbyte)
{
- static char str[11];
- if (!h->configured)
- snprintf(str, sizeof(str), "INVALID");
- else
- snprintf(str, sizeof(str), "%u %d%c%d",
- serial_get_baud_int (h->baud ),
- serial_get_bits_int (h->bits ),
- serial_get_parity_str (h->parity ),
- serial_get_stopbit_int(h->stopbit)
- );
-
- return str;
+ serial_t *h;
+ DWORD r;
+ uint8_t *pos = (uint8_t *)buf;
+
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+
+ while (nbyte) {
+ if (!WriteFile(h->fd, pos, nbyte, &r, NULL))
+ return PORT_ERR_UNKNOWN;
+ if (r < 1)
+ return PORT_ERR_UNKNOWN;
+
+ nbyte -= r;
+ pos += r;
+ }
+ return PORT_ERR_OK;
}
-serial_err_t serial_gpio (const serial_t *h, serial_gpio_t n, int level) {
+static port_err_t serial_w32_gpio(struct port_interface *port,
+ serial_gpio_t n, int level)
+{
+ serial_t *h;
int bit;
- switch(n) {
- case GPIO_RTS:
- bit = level ? SETRTS : CLRRTS;
- break;
-
- case GPIO_DTR:
- bit = level ? SETDTR : CLRDTR;
- break;
-
- case GPIO_BRK:
- if (level == 0)
- return SERIAL_ERR_OK;
- if (EscapeCommFunction(h->fd, SETBREAK) == 0)
- return SERIAL_ERR_SYSTEM;
- usleep(500000);
- if (EscapeCommFunction(h->fd, CLRBREAK) == 0)
- return SERIAL_ERR_SYSTEM;
- return SERIAL_ERR_OK;
-
- default:
- return SERIAL_ERR_NODATA;
- }
+ h = (serial_t *)port->private;
+ if (h == NULL)
+ return PORT_ERR_UNKNOWN;
+
+ switch (n) {
+ case GPIO_RTS:
+ bit = level ? SETRTS : CLRRTS;
+ break;
+
+ case GPIO_DTR:
+ bit = level ? SETDTR : CLRDTR;
+ break;
+
+ case GPIO_BRK:
+ if (level == 0)
+ return PORT_ERR_OK;
+ if (EscapeCommFunction(h->fd, SETBREAK) == 0)
+ return PORT_ERR_UNKNOWN;
+ usleep(500000);
+ if (EscapeCommFunction(h->fd, CLRBREAK) == 0)
+ return PORT_ERR_UNKNOWN;
+ return PORT_ERR_OK;
+
+ default:
+ return PORT_ERR_UNKNOWN;
+ }
/* handle RTS/DTR */
if (EscapeCommFunction(h->fd, bit) == 0)
- return SERIAL_ERR_SYSTEM;
+ return PORT_ERR_UNKNOWN;
- return SERIAL_ERR_OK;
+ return PORT_ERR_OK;
}
+
+static const char *serial_w32_get_cfg_str(struct port_interface *port)
+{
+ serial_t *h;
+
+ h = (serial_t *)port->private;
+ return h ? h->setup_str : "INVALID";
+}
+
+struct port_interface port_serial = {
+ .name = "serial_w32",
+ .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY,
+ .open = serial_w32_open,
+ .close = serial_w32_close,
+ .read = serial_w32_read,
+ .write = serial_w32_write,
+ .gpio = serial_w32_gpio,
+ .get_cfg_str = serial_w32_get_cfg_str,
+};
diff --git a/stm32.c b/stm32.c
index b2b3dc6..ca95b3e 100644
--- a/stm32.c
+++ b/stm32.c
@@ -19,15 +19,20 @@
*/
#include <stdlib.h>
-#include <assert.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
+#include <time.h>
+#include <unistd.h>
#include "stm32.h"
+#include "port.h"
#include "utils.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 */
@@ -35,14 +40,31 @@
#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 35 /* seconds */
+#define STM32_MASSERASE_TIMEOUT 35 /* seconds */
+#define STM32_SECTERASE_TIMEOUT 5 /* seconds */
+#define STM32_BLKWRITE_TIMEOUT 1 /* seconds */
+#define STM32_WUNPROT_TIMEOUT 1 /* seconds */
+#define STM32_WPROT_TIMEOUT 1 /* seconds */
+#define STM32_RPROT_TIMEOUT 1 /* seconds */
+
+#define STM32_CMD_GET_LENGTH 17 /* bytes in the reply */
+
struct stm32_cmd {
uint8_t get;
uint8_t gvr;
@@ -55,6 +77,7 @@ struct stm32_cmd {
uint8_t uw;
uint8_t rp;
uint8_t ur;
+ uint8_t crc;
};
/* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0)
@@ -72,129 +95,288 @@ static const uint8_t stm_reset_code[] = {
static const uint32_t stm_reset_code_length = sizeof(stm_reset_code);
-/* Device table, corresponds to the "Bootloader device-dependent parameters"
- * table in ST document AN2606.
- * Note that the option bytes upper range is inclusive!
- */
-const stm32_dev_t devices[] = {
- /* F0 */
- {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800},
- {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800},
- {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800},
- /* F1 */
- {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
- {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
- {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
- {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
- {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
- {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800},
- {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800},
- /* Note that F2 and F4 devices have sectors of different page sizes
- and only the first sectors (of one page size) are included here */
- /* F2 */
- {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF},
- /* F3 */
- {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
- {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
- {0x439, "STM32F302" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
- {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800},
- /* F4 */
- {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF},
- /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */
- {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF},
- {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF},
- {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF},
- /* L0 */
- {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000},
- /* L1 */
- {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000},
- {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000},
- {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000},
- {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000},
- {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000},
- /* These are not (yet) in AN2606: */
- {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800},
- {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0},
- {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0},
- {0x0}
-};
+extern const stm32_dev_t devices[];
+
+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");
+}
+
+static stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, time_t timeout)
+{
+ struct port_interface *port = stm->port;
+ uint8_t byte;
+ port_err_t p_err;
+ time_t t0, t1;
+
+ if (!(port->flags & PORT_RETRY))
+ timeout = 0;
-/* internal functions */
-uint8_t stm32_gen_cs(const uint32_t v);
-void stm32_send_byte(const stm32_t *stm, uint8_t byte);
-uint8_t stm32_read_byte(const stm32_t *stm);
-char stm32_send_command(const stm32_t *stm, const uint8_t cmd);
+ if (timeout)
+ time(&t0);
+ do {
+ p_err = port->read(port, &byte, 1);
+ if (p_err == PORT_ERR_TIMEDOUT && timeout) {
+ time(&t1);
+ if (t1 < t0 + timeout)
+ continue;
+ }
+
+ if (p_err != PORT_ERR_OK) {
+ fprintf(stderr, "Failed to read ACK byte\n");
+ return STM32_ERR_UNKNOWN;
+ }
+
+ if (byte == STM32_ACK)
+ return STM32_ERR_OK;
+ if (byte == STM32_NACK)
+ return STM32_ERR_NACK;
+ if (byte != STM32_BUSY) {
+ fprintf(stderr, "Got byte 0x%02x instead of ACK\n",
+ byte);
+ return STM32_ERR_UNKNOWN;
+ }
+ } while (1);
+}
-uint8_t stm32_gen_cs(const uint32_t v) {
- return ((v & 0xFF000000) >> 24) ^
- ((v & 0x00FF0000) >> 16) ^
- ((v & 0x0000FF00) >> 8) ^
- ((v & 0x000000FF) >> 0);
+static stm32_err_t stm32_get_ack(const stm32_t *stm)
+{
+ return stm32_get_ack_timeout(stm, 0);
}
-void stm32_send_byte(const stm32_t *stm, uint8_t byte) {
- serial_err_t err;
- err = serial_write(stm->serial, &byte, 1);
- if (err != SERIAL_ERR_OK) {
- fprintf(stderr, "Failed to send byte: ");
- perror("send_byte");
- exit(1);
+static stm32_err_t stm32_send_command_timeout(const stm32_t *stm,
+ const uint8_t cmd,
+ time_t timeout)
+{
+ struct port_interface *port = stm->port;
+ stm32_err_t s_err;
+ port_err_t p_err;
+ uint8_t buf[2];
+
+ buf[0] = cmd;
+ buf[1] = cmd ^ 0xFF;
+ p_err = port->write(port, buf, 2);
+ if (p_err != PORT_ERR_OK) {
+ fprintf(stderr, "Failed to send command\n");
+ return STM32_ERR_UNKNOWN;
}
+ s_err = stm32_get_ack_timeout(stm, timeout);
+ if (s_err == STM32_ERR_OK)
+ return STM32_ERR_OK;
+ if (s_err == STM32_ERR_NACK)
+ fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd);
+ else
+ fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd);
+ return STM32_ERR_UNKNOWN;
}
-uint8_t stm32_read_byte(const stm32_t *stm) {
- uint8_t byte;
- serial_err_t err;
- err = serial_read(stm->serial, &byte, 1);
- if (err != SERIAL_ERR_OK) {
- fprintf(stderr, "Failed to read byte: ");
- perror("read_byte");
- exit(1);
- }
- return byte;
+static stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd)
+{
+ return stm32_send_command_timeout(stm, cmd, 0);
}
-char stm32_send_command(const stm32_t *stm, const uint8_t cmd) {
- int ret;
+/* if we have lost sync, send a wrong command and expect a NACK */
+static stm32_err_t stm32_resync(const stm32_t *stm)
+{
+ struct port_interface *port = stm->port;
+ port_err_t p_err;
+ uint8_t buf[2], ack;
+ time_t t0, t1;
+
+ time(&t0);
+ t1 = t0;
+
+ buf[0] = STM32_CMD_ERR;
+ buf[1] = STM32_CMD_ERR ^ 0xFF;
+ while (t1 < t0 + STM32_RESYNC_TIMEOUT) {
+ p_err = port->write(port, buf, 2);
+ if (p_err != PORT_ERR_OK) {
+ usleep(500000);
+ time(&t1);
+ continue;
+ }
+ p_err = port->read(port, &ack, 1);
+ if (p_err != PORT_ERR_OK) {
+ time(&t1);
+ continue;
+ }
+ if (ack == STM32_NACK)
+ return STM32_ERR_OK;
+ time(&t1);
+ }
+ return STM32_ERR_UNKNOWN;
+}
- stm32_send_byte(stm, cmd);
- stm32_send_byte(stm, cmd ^ 0xFF);
- ret = stm32_read_byte(stm);
- if (ret == STM32_ACK) {
- return 1;
- } else if (ret == STM32_NACK) {
- fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd);
- } else {
- fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd);
+/*
+ * 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.
+ */
+static stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, uint8_t cmd,
+ uint8_t *data, unsigned int len)
+{
+ struct port_interface *port = stm->port;
+ port_err_t p_err;
+
+ if (stm32_send_command(stm, cmd) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ if (port->flags & PORT_BYTE) {
+ /* interface is UART-like */
+ p_err = port->read(port, data, 1);
+ if (p_err != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ len = data[0];
+ p_err = port->read(port, data + 1, len + 1);
+ if (p_err != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ return STM32_ERR_OK;
+ }
+
+ p_err = port->read(port, data, len + 2);
+ if (p_err == PORT_ERR_OK && len == data[0])
+ return STM32_ERR_OK;
+ if (p_err != PORT_ERR_OK) {
+ /* restart with only one byte */
+ if (stm32_resync(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ if (stm32_send_command(stm, cmd) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ p_err = port->read(port, data, 1);
+ if (p_err != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ }
+
+ fprintf(stderr, "Re sync (len = %d)\n", data[0]);
+ if (stm32_resync(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ len = data[0];
+ if (stm32_send_command(stm, cmd) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ p_err = port->read(port, data, len + 2);
+ if (p_err != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ return STM32_ERR_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.
+ */
+static stm32_err_t stm32_send_init_seq(const stm32_t *stm)
+{
+ struct port_interface *port = stm->port;
+ port_err_t p_err;
+ uint8_t byte, cmd = STM32_CMD_INIT;
+
+ p_err = port->write(port, &cmd, 1);
+ if (p_err != PORT_ERR_OK) {
+ fprintf(stderr, "Failed to send init to device\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ p_err = port->read(port, &byte, 1);
+ if (p_err == PORT_ERR_OK && byte == STM32_ACK)
+ return STM32_ERR_OK;
+ if (p_err == PORT_ERR_OK && 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 STM32_ERR_OK;
+ }
+ if (p_err != PORT_ERR_TIMEDOUT) {
+ fprintf(stderr, "Failed to init device.\n");
+ return STM32_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(port, &cmd, 1);
+ if (p_err != PORT_ERR_OK) {
+ fprintf(stderr, "Failed to send init to device\n");
+ return STM32_ERR_UNKNOWN;
}
- return 0;
+ p_err = port->read(port, &byte, 1);
+ if (p_err == PORT_ERR_OK && byte == STM32_NACK)
+ return STM32_ERR_OK;
+ fprintf(stderr, "Failed to init device.\n");
+ return STM32_ERR_UNKNOWN;
}
-stm32_t* stm32_init(const serial_t *serial, const char init) {
- uint8_t len, val;
+/* find newer command by higher code */
+#define newer(prev, a) (((prev) == STM32_CMD_ERR) \
+ ? (a) \
+ : (((prev) > (a)) ? (prev) : (a)))
+
+stm32_t *stm32_init(struct port_interface *port, const char init)
+{
+ uint8_t len, val, buf[257];
stm32_t *stm;
+ int i, new_cmds;
stm = calloc(sizeof(stm32_t), 1);
stm->cmd = malloc(sizeof(stm32_cmd_t));
memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t));
- stm->serial = serial;
+ stm->port = port;
- if (init) {
- stm32_send_byte(stm, STM32_CMD_INIT);
- if (stm32_read_byte(stm) != STM32_ACK) {
- stm32_close(stm);
- fprintf(stderr, "Failed to get init ACK from device\n");
+ if ((port->flags & PORT_CMD_INIT) && init)
+ if (stm32_send_init_seq(stm) != STM32_ERR_OK)
return NULL;
- }
+
+ /* get the version and read protection status */
+ if (stm32_send_command(stm, STM32_CMD_GVR) != STM32_ERR_OK) {
+ stm32_close(stm);
+ return NULL;
+ }
+
+ /* From AN, only UART bootloader returns 3 bytes */
+ len = (port->flags & PORT_GVR_ETX) ? 3 : 1;
+ if (port->read(port, buf, len) != PORT_ERR_OK)
+ return NULL;
+ stm->version = buf[0];
+ stm->option1 = (port->flags & PORT_GVR_ETX) ? buf[1] : 0;
+ stm->option2 = (port->flags & PORT_GVR_ETX) ? buf[2] : 0;
+ if (stm32_get_ack(stm) != STM32_ERR_OK) {
+ stm32_close(stm);
+ return NULL;
}
/* get the bootloader information */
- if (!stm32_send_command(stm, STM32_CMD_GET)) return 0;
- len = stm32_read_byte(stm) + 1;
- stm->bl_version = stm32_read_byte(stm); --len;
- while (len-- > 0) {
- val = stm32_read_byte(stm);
+ len = STM32_CMD_GET_LENGTH;
+ if (port->cmd_get_reply)
+ for (i = 0; port->cmd_get_reply[i].length; i++)
+ if (stm->version == port->cmd_get_reply[i].version) {
+ len = port->cmd_get_reply[i].length;
+ break;
+ }
+ if (stm32_guess_len_cmd(stm, STM32_CMD_GET, buf, len) != STM32_ERR_OK)
+ return NULL;
+ len = buf[0] + 1;
+ stm->bl_version = buf[1];
+ new_cmds = 0;
+ for (i = 1; i < len; i++) {
+ val = buf[i + 1];
switch (val) {
case STM32_CMD_GET:
stm->cmd->get = val; break;
@@ -207,26 +389,45 @@ stm32_t* stm32_init(const serial_t *serial, const char init) {
case STM32_CMD_GO:
stm->cmd->go = val; break;
case STM32_CMD_WM:
- stm->cmd->wm = val; break;
+ case STM32_CMD_WM_NS:
+ stm->cmd->wm = newer(stm->cmd->wm, val);
+ break;
case STM32_CMD_ER:
case STM32_CMD_EE:
- stm->cmd->er = val; break;
+ case STM32_CMD_EE_NS:
+ stm->cmd->er = newer(stm->cmd->er, val);
+ break;
case STM32_CMD_WP:
- stm->cmd->wp = val; break;
+ case STM32_CMD_WP_NS:
+ stm->cmd->wp = newer(stm->cmd->wp, val);
+ break;
case STM32_CMD_UW:
- stm->cmd->uw = val; break;
+ case STM32_CMD_UW_NS:
+ stm->cmd->uw = newer(stm->cmd->uw, val);
+ break;
case STM32_CMD_RP:
- stm->cmd->rp = val; break;
+ case STM32_CMD_RP_NS:
+ stm->cmd->rp = newer(stm->cmd->rp, val);
+ break;
case STM32_CMD_UR:
- stm->cmd->ur = val; break;
+ case STM32_CMD_UR_NS:
+ stm->cmd->ur = newer(stm->cmd->ur, val);
+ break;
+ case STM32_CMD_CRC:
+ stm->cmd->crc = newer(stm->cmd->crc, val);
+ break;
default:
- fprintf(stderr,
- "Seems this bootloader returns more then we "
- "understand in the GET command.\n"
- "We will skip unknown byte 0x%02x\n", val);
+ if (new_cmds++ == 0)
+ fprintf(stderr,
+ "GET returns unknown commands (0x%2x",
+ val);
+ else
+ fprintf(stderr, ", 0x%2x", val);
}
}
- if (stm32_read_byte(stm) != STM32_ACK) {
+ if (new_cmds)
+ fprintf(stderr, ")\n");
+ if (stm32_get_ack(stm) != STM32_ERR_OK) {
stm32_close(stm);
return NULL;
}
@@ -234,51 +435,35 @@ stm32_t* stm32_init(const serial_t *serial, const char init) {
if (stm->cmd->get == STM32_CMD_ERR
|| stm->cmd->gvr == STM32_CMD_ERR
|| stm->cmd->gid == STM32_CMD_ERR) {
- fprintf(stderr, "Error: bootloader did not returned correct "
- "information from GET command\n");
- return NULL;
- }
-
- /* get the version and read protection status */
- if (!stm32_send_command(stm, stm->cmd->gvr)) {
- stm32_close(stm);
- return NULL;
- }
-
- stm->version = stm32_read_byte(stm);
- stm->option1 = stm32_read_byte(stm);
- stm->option2 = stm32_read_byte(stm);
- if (stm32_read_byte(stm) != STM32_ACK) {
- stm32_close(stm);
+ fprintf(stderr, "Error: bootloader did not returned correct information from GET command\n");
return NULL;
}
/* get the device ID */
- if (!stm32_send_command(stm, stm->cmd->gid)) {
+ if (stm32_guess_len_cmd(stm, stm->cmd->gid, buf, 1) != STM32_ERR_OK) {
stm32_close(stm);
return NULL;
}
- len = stm32_read_byte(stm) + 1;
+ len = buf[0] + 1;
if (len < 2) {
stm32_close(stm);
fprintf(stderr, "Only %d bytes sent in the PID, unknown/unsupported device\n", len);
return NULL;
}
- stm->pid = (stm32_read_byte(stm) << 8) | stm32_read_byte(stm);
- len -= 2;
- if (len > 0) {
+ stm->pid = (buf[1] << 8) | buf[2];
+ if (len > 2) {
fprintf(stderr, "This bootloader returns %d extra bytes in PID:", len);
- while (len-- > 0)
- fprintf(stderr, " %02x", stm32_read_byte(stm));
+ for (i = 2; i <= len ; i++)
+ fprintf(stderr, " %02x", buf[i]);
fprintf(stderr, "\n");
}
- if (stm32_read_byte(stm) != STM32_ACK) {
+ if (stm32_get_ack(stm) != STM32_ERR_OK) {
stm32_close(stm);
return NULL;
}
stm->dev = devices;
- while(stm->dev->id != 0x00 && stm->dev->id != stm->pid)
+ while (stm->dev->id != 0x00 && stm->dev->id != stm->pid)
++stm->dev;
if (!stm->dev->id) {
@@ -290,264 +475,574 @@ stm32_t* stm32_init(const serial_t *serial, const char init) {
return stm;
}
-void stm32_close(stm32_t *stm) {
- if (stm) free(stm->cmd);
+void stm32_close(stm32_t *stm)
+{
+ if (stm)
+ free(stm->cmd);
free(stm);
}
-char stm32_read_memory(const stm32_t *stm, uint32_t address, uint8_t data[], unsigned int len) {
- uint8_t cs;
- unsigned int i;
- assert(len > 0 && len < 257);
+stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address,
+ uint8_t data[], unsigned int len)
+{
+ struct port_interface *port = stm->port;
+ uint8_t buf[5];
- /* must be 32bit aligned */
- assert(address % 4 == 0);
+ if (!len)
+ return STM32_ERR_OK;
- address = be_u32 (address);
- cs = stm32_gen_cs(address);
+ if (len > 256) {
+ fprintf(stderr, "Error: READ length limit at 256 bytes\n");
+ return STM32_ERR_UNKNOWN;
+ }
if (stm->cmd->rm == STM32_CMD_ERR) {
fprintf(stderr, "Error: READ command not implemented in bootloader.\n");
- return 0;
+ return STM32_ERR_NO_CMD;
}
- if (!stm32_send_command(stm, stm->cmd->rm)) return 0;
- if (serial_write(stm->serial, &address, 4) != SERIAL_ERR_OK)
- return 0;
+ if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
- stm32_send_byte(stm, cs);
- if (stm32_read_byte(stm) != STM32_ACK) return 0;
+ buf[0] = address >> 24;
+ buf[1] = (address >> 16) & 0xFF;
+ buf[2] = (address >> 8) & 0xFF;
+ buf[3] = address & 0xFF;
+ buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
+ if (port->write(port, buf, 5) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ if (stm32_get_ack(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
- i = len - 1;
- stm32_send_byte(stm, i);
- stm32_send_byte(stm, i ^ 0xFF);
- if (stm32_read_byte(stm) != STM32_ACK) return 0;
+ if (stm32_send_command(stm, len - 1) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
- if (serial_read(stm->serial, data, len) != SERIAL_ERR_OK)
- return 0;
+ if (port->read(port, data, len) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
- return 1;
+ return STM32_ERR_OK;
}
-char stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t data[], unsigned int len) {
- uint8_t cs;
- unsigned int i;
- int c, extra;
- assert(len > 0 && len < 257);
+stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address,
+ const uint8_t data[], unsigned int len)
+{
+ struct port_interface *port = stm->port;
+ uint8_t cs, buf[256 + 2];
+ unsigned int i, aligned_len;
+ stm32_err_t s_err;
- /* must be 32bit aligned */
- assert(address % 4 == 0);
+ if (!len)
+ return STM32_ERR_OK;
- address = be_u32 (address);
- cs = stm32_gen_cs(address);
+ if (len > 256) {
+ fprintf(stderr, "Error: READ length limit at 256 bytes\n");
+ return STM32_ERR_UNKNOWN;
+ }
+
+ /* must be 32bit aligned */
+ if (address & 0x3 || len & 0x3) {
+ fprintf(stderr, "Error: WRITE address and length must be 4 byte aligned\n");
+ return STM32_ERR_UNKNOWN;
+ }
if (stm->cmd->wm == STM32_CMD_ERR) {
fprintf(stderr, "Error: WRITE command not implemented in bootloader.\n");
- return 0;
+ return STM32_ERR_NO_CMD;
}
/* send the address and checksum */
- if (!stm32_send_command(stm, stm->cmd->wm)) return 0;
- if (serial_write(stm->serial, &address, 4) != SERIAL_ERR_OK)
- return 0;
-
- stm32_send_byte(stm, cs);
- if (stm32_read_byte(stm) != STM32_ACK) return 0;
-
- /* setup the cs and send the length */
- extra = len % 4;
- cs = len - 1 + extra;
- stm32_send_byte(stm, cs);
-
- /* write the data and build the checksum */
- for(i = 0; i < len; ++i)
+ if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ buf[0] = address >> 24;
+ buf[1] = (address >> 16) & 0xFF;
+ buf[2] = (address >> 8) & 0xFF;
+ buf[3] = address & 0xFF;
+ buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
+ if (port->write(port, buf, 5) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ if (stm32_get_ack(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ aligned_len = (len + 3) & ~3;
+ cs = aligned_len - 1;
+ buf[0] = aligned_len - 1;
+ for (i = 0; i < len; i++) {
cs ^= data[i];
-
- if (serial_write(stm->serial, data, len) != SERIAL_ERR_OK)
- return 0;
-
- /* write the alignment padding */
- for(c = 0; c < extra; ++c) {
- stm32_send_byte(stm, 0xFF);
+ buf[i + 1] = data[i];
+ }
+ /* padding data */
+ for (i = len; i < aligned_len; i++) {
cs ^= 0xFF;
+ buf[i + 1] = 0xFF;
}
-
- /* send the checksum */
- stm32_send_byte(stm, cs);
- return stm32_read_byte(stm) == STM32_ACK;
+ buf[aligned_len + 1] = cs;
+ if (port->write(port, buf, aligned_len + 2) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT);
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->wm != STM32_CMD_WM_NS)
+ stm32_warn_stretching("write");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
}
-char stm32_wunprot_memory(const stm32_t *stm) {
+stm32_err_t stm32_wunprot_memory(const stm32_t *stm)
+{
+ struct port_interface *port = stm->port;
+ stm32_err_t s_err;
+
if (stm->cmd->uw == STM32_CMD_ERR) {
fprintf(stderr, "Error: WRITE UNPROTECT command not implemented in bootloader.\n");
- return 0;
+ return STM32_ERR_NO_CMD;
+ }
+
+ if (stm32_send_command(stm, stm->cmd->uw) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ s_err = stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT);
+ if (s_err == STM32_NACK) {
+ fprintf(stderr, "Error: Failed to WRITE UNPROTECT\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->uw != STM32_CMD_UW_NS)
+ stm32_warn_stretching("WRITE UNPROTECT");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
+}
+
+stm32_err_t stm32_wprot_memory(const stm32_t *stm)
+{
+ struct port_interface *port = stm->port;
+ stm32_err_t s_err;
+
+ if (stm->cmd->wp == STM32_CMD_ERR) {
+ fprintf(stderr, "Error: WRITE PROTECT command not implemented in bootloader.\n");
+ return STM32_ERR_NO_CMD;
}
- if (!stm32_send_command(stm, stm->cmd->uw)) return 0;
- if (!stm32_send_command(stm, 0x8C )) return 0;
- return 1;
+ if (stm32_send_command(stm, stm->cmd->wp) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ s_err = stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT);
+ if (s_err == STM32_NACK) {
+ fprintf(stderr, "Error: Failed to WRITE PROTECT\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->wp != STM32_CMD_WP_NS)
+ stm32_warn_stretching("WRITE PROTECT");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
}
-char stm32_runprot_memory (const stm32_t *stm) {
+stm32_err_t stm32_runprot_memory(const stm32_t *stm)
+{
+ struct port_interface *port = stm->port;
+ stm32_err_t s_err;
+
if (stm->cmd->ur == STM32_CMD_ERR) {
- fprintf(stderr, "Error: READ UNPROTECT command not implemented in bootloader.\n");
- return 0;
+ fprintf(stderr, "Error: READOUT UNPROTECT command not implemented in bootloader.\n");
+ return STM32_ERR_NO_CMD;
}
- if (!stm32_send_command(stm, stm->cmd->ur)) return 0;
- if (!stm32_send_command(stm, 0x6D )) return 0;
- return 1;
+ if (stm32_send_command(stm, stm->cmd->ur) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT);
+ if (s_err == STM32_NACK) {
+ fprintf(stderr, "Error: Failed to READOUT UNPROTECT\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->ur != STM32_CMD_UR_NS)
+ stm32_warn_stretching("READOUT UNPROTECT");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
}
-char stm32_readprot_memory(const stm32_t *stm) {
+stm32_err_t stm32_readprot_memory(const stm32_t *stm)
+{
+ struct port_interface *port = stm->port;
+ stm32_err_t s_err;
+
if (stm->cmd->rp == STM32_CMD_ERR) {
- fprintf(stderr, "Error: READ PROTECT command not implemented in bootloader.\n");
- return 0;
+ fprintf(stderr, "Error: READOUT PROTECT command not implemented in bootloader.\n");
+ return STM32_ERR_NO_CMD;
}
- if (!stm32_send_command(stm, stm->cmd->rp)) return 0;
- if (!stm32_send_command(stm, 0x7D )) return 0;
- return 1;
+ if (stm32_send_command(stm, stm->cmd->rp) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ s_err = stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT);
+ if (s_err == STM32_NACK) {
+ fprintf(stderr, "Error: Failed to READOUT PROTECT\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->rp != STM32_CMD_RP_NS)
+ stm32_warn_stretching("READOUT PROTECT");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
}
-char stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) {
+stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages)
+{
+ struct port_interface *port = stm->port;
+ stm32_err_t s_err;
+ port_err_t p_err;
if (!pages)
- return 1;
+ return STM32_ERR_OK;
if (stm->cmd->er == STM32_CMD_ERR) {
fprintf(stderr, "Error: ERASE command not implemented in bootloader.\n");
- return 0;
+ return STM32_ERR_NO_CMD;
}
- if (!stm32_send_command(stm, stm->cmd->er)) {
+ if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) {
fprintf(stderr, "Can't initiate chip erase!\n");
- return 0;
+ return STM32_ERR_UNKNOWN;
}
- /* The erase command reported by the bootloader is either 0x43 or 0x44 */
+ /* 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. */
- if (stm->cmd->er == STM32_CMD_EE) {
- /* Not all chips using Extended Erase support mass erase */
- /* Currently known as not supporting mass erase is the Ultra Low Power STM32L15xx range */
- /* So if someone has not overridden the default, but uses one of these chips, take it out of */
- /* mass erase mode, so it will be done page by page. This maximum might not be correct either! */
- if (stm->pid == 0x416 && pages == 0xFF) pages = 0xF8; /* works for the STM32L152RB with 128Kb flash */
+ /* 0x45 is clock no-stretching version of Extended Erase for I2C port. */
+ if (stm->cmd->er != STM32_CMD_ER) {
+ /* Not all chips using Extended Erase support mass erase */
+ /* Currently known as not supporting mass erase is the Ultra Low Power STM32L15xx range */
+ /* So if someone has not overridden the default, but uses one of these chips, take it out of */
+ /* mass erase mode, so it will be done page by page. This maximum might not be correct either! */
+ if (stm->pid == 0x416 && pages == 0xFF)
+ pages = 0xF8; /* works for the STM32L152RB with 128Kb flash */
if (pages == 0xFF) {
- stm32_send_byte(stm, 0xFF);
- stm32_send_byte(stm, 0xFF); // 0xFFFF the magic number for mass erase
- stm32_send_byte(stm, 0x00); // 0x00 the XOR of those two bytes as a checksum
- if (stm32_read_byte(stm) != STM32_ACK) {
+ uint8_t buf[3];
+
+ /* 0xFFFF the magic number for mass erase */
+ buf[0] = 0xFF;
+ buf[1] = 0xFF;
+ buf[2] = 0x00; /* checksum */
+ if (port->write(port, buf, 3) != PORT_ERR_OK) {
+ fprintf(stderr, "Mass erase error.\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT);
+ if (s_err != STM32_ERR_OK) {
fprintf(stderr, "Mass erase failed. Try specifying the number of pages to be erased.\n");
- return 0;
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->er != STM32_CMD_EE_NS)
+ stm32_warn_stretching("erase");
+ return STM32_ERR_UNKNOWN;
}
- return 1;
+ return STM32_ERR_OK;
}
uint16_t pg_num;
uint8_t pg_byte;
- uint8_t cs = 0;
-
+ uint8_t cs = 0;
+ uint8_t *buf;
+ int i = 0;
+
+ buf = malloc(2 + 2 * pages + 1);
+ if (!buf)
+ return STM32_ERR_UNKNOWN;
+
+ /* Number of pages to be erased - 1, two bytes, MSB first */
pg_byte = (pages - 1) >> 8;
- stm32_send_byte(stm, pg_byte); // Number of pages to be erased - 1, two bytes, MSB first
+ buf[i++] = pg_byte;
cs ^= pg_byte;
pg_byte = (pages - 1) & 0xFF;
- stm32_send_byte(stm, pg_byte);
+ buf[i++] = pg_byte;
cs ^= pg_byte;
-
+
for (pg_num = spage; pg_num < spage + pages; pg_num++) {
- pg_byte = pg_num >> 8;
- cs ^= pg_byte;
- stm32_send_byte(stm, pg_byte);
- pg_byte = pg_num & 0xFF;
- cs ^= pg_byte;
- stm32_send_byte(stm, pg_byte);
- }
- stm32_send_byte(stm, cs);
-
- if (stm32_read_byte(stm) != STM32_ACK) {
- fprintf(stderr, "Page-by-page erase failed. Check the maximum pages your device supports.\n");
- return 0;
- }
-
- return 1;
+ pg_byte = pg_num >> 8;
+ cs ^= pg_byte;
+ buf[i++] = pg_byte;
+ pg_byte = pg_num & 0xFF;
+ cs ^= pg_byte;
+ buf[i++] = pg_byte;
+ }
+ buf[i++] = cs;
+ p_err = port->write(port, buf, i);
+ free(buf);
+ if (p_err != PORT_ERR_OK) {
+ fprintf(stderr, "Page-by-page erase error.\n");
+ return STM32_ERR_UNKNOWN;
+ }
+
+ s_err = stm32_get_ack_timeout(stm, STM32_SECTERASE_TIMEOUT);
+ if (s_err != STM32_ERR_OK) {
+ fprintf(stderr, "Page-by-page erase failed. Check the maximum pages your device supports.\n");
+ if (port->flags & PORT_STRETCH_W
+ && stm->cmd->er != STM32_CMD_EE_NS)
+ stm32_warn_stretching("erase");
+ return STM32_ERR_UNKNOWN;
+ }
+
+ return STM32_ERR_OK;
}
/* And now the regular erase (0x43) for all other chips */
if (pages == 0xFF) {
- return stm32_send_command(stm, 0xFF);
+ s_err = stm32_send_command_timeout(stm, 0xFF, STM32_MASSERASE_TIMEOUT);
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W)
+ stm32_warn_stretching("erase");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
} else {
uint8_t cs = 0;
uint8_t pg_num;
- stm32_send_byte(stm, pages-1);
+ uint8_t *buf;
+ int i = 0;
+
+ buf = malloc(1 + pages + 1);
+ if (!buf)
+ return STM32_ERR_UNKNOWN;
+
+ buf[i++] = pages - 1;
cs ^= (pages-1);
for (pg_num = spage; pg_num < (pages + spage); pg_num++) {
- stm32_send_byte(stm, pg_num);
+ buf[i++] = pg_num;
cs ^= pg_num;
}
- stm32_send_byte(stm, cs);
- return stm32_read_byte(stm) == STM32_ACK;
+ buf[i++] = cs;
+ p_err = port->write(port, buf, i);
+ free(buf);
+ if (p_err != PORT_ERR_OK) {
+ fprintf(stderr, "Erase failed.\n");
+ return STM32_ERR_UNKNOWN;
+ }
+ s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT);
+ if (s_err != STM32_ERR_OK) {
+ if (port->flags & PORT_STRETCH_W)
+ stm32_warn_stretching("erase");
+ return STM32_ERR_UNKNOWN;
+ }
+ return STM32_ERR_OK;
}
}
-char stm32_run_raw_code(const stm32_t *stm, uint32_t target_address, const uint8_t *code, uint32_t code_size)
+static stm32_err_t stm32_run_raw_code(const stm32_t *stm,
+ uint32_t target_address,
+ const uint8_t *code, uint32_t code_size)
{
uint32_t stack_le = le_u32(0x20002000);
uint32_t code_address_le = le_u32(target_address + 8);
uint32_t length = code_size + 8;
-
+ uint8_t *mem, *pos;
+ uint32_t address, w;
+
/* Must be 32-bit aligned */
- assert(target_address % 4 == 0);
+ if (target_address & 0x3) {
+ fprintf(stderr, "Error: code address must be 4 byte aligned\n");
+ return STM32_ERR_UNKNOWN;
+ }
- uint8_t *mem = malloc(length);
+ mem = malloc(length);
if (!mem)
- return 0;
-
+ return STM32_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;
- uint32_t address = target_address;
- while(length > 0) {
-
- uint32_t w = length > 256 ? 256 : length;
- if (!stm32_write_memory(stm, address, pos, w)) {
+
+ pos = mem;
+ address = target_address;
+ while (length > 0) {
+ w = length > 256 ? 256 : length;
+ if (stm32_write_memory(stm, address, pos, w) != STM32_ERR_OK) {
free(mem);
- return 0;
+ return STM32_ERR_UNKNOWN;
}
-
+
address += w;
pos += w;
- length -=w;
+ length -= w;
}
-
+
free(mem);
return stm32_go(stm, target_address);
}
-char stm32_go(const stm32_t *stm, uint32_t address) {
- uint8_t cs;
+stm32_err_t stm32_go(const stm32_t *stm, uint32_t address)
+{
+ struct port_interface *port = stm->port;
+ uint8_t buf[5];
- address = be_u32 (address);
- cs = stm32_gen_cs(address);
+ if (stm->cmd->go == STM32_CMD_ERR) {
+ fprintf(stderr, "Error: GO command not implemented in bootloader.\n");
+ return STM32_ERR_NO_CMD;
+ }
- if (stm->cmd->go == STM32_CMD_ERR) {
- fprintf(stderr, "Error: GO command not implemented in bootloader.\n");
- return 0;
- }
+ if (stm32_send_command(stm, stm->cmd->go) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
- if (!stm32_send_command(stm, stm->cmd->go)) return 0;
- serial_write(stm->serial, &address, 4);
- serial_write(stm->serial, &cs , 1);
+ buf[0] = address >> 24;
+ buf[1] = (address >> 16) & 0xFF;
+ buf[2] = (address >> 8) & 0xFF;
+ buf[3] = address & 0xFF;
+ buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
+ if (port->write(port, buf, 5) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
- return stm32_read_byte(stm) == STM32_ACK;
+ if (stm32_get_ack(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+ return STM32_ERR_OK;
}
-char stm32_reset_device(const stm32_t *stm) {
+stm32_err_t stm32_reset_device(const stm32_t *stm)
+{
uint32_t target_address = stm->dev->ram_start;
-
+
return stm32_run_raw_code(stm, target_address, stm_reset_code, stm_reset_code_length);
}
+stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address,
+ uint32_t length, uint32_t *crc)
+{
+ struct port_interface *port = stm->port;
+ uint8_t buf[5];
+
+ if (address & 0x3 || length & 0x3) {
+ fprintf(stderr, "Start and end addresses must be 4 byte aligned\n");
+ return STM32_ERR_UNKNOWN;
+ }
+
+ if (stm->cmd->crc == STM32_CMD_ERR) {
+ fprintf(stderr, "Error: CRC command not implemented in bootloader.\n");
+ return STM32_ERR_NO_CMD;
+ }
+
+ if (stm32_send_command(stm, stm->cmd->crc) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ buf[0] = address >> 24;
+ buf[1] = (address >> 16) & 0xFF;
+ buf[2] = (address >> 8) & 0xFF;
+ buf[3] = address & 0xFF;
+ buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
+ if (port->write(port, buf, 5) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ if (stm32_get_ack(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ buf[0] = length >> 24;
+ buf[1] = (length >> 16) & 0xFF;
+ buf[2] = (length >> 8) & 0xFF;
+ buf[3] = length & 0xFF;
+ buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
+ if (port->write(port, buf, 5) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ if (stm32_get_ack(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ if (stm32_get_ack(stm) != STM32_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ if (port->read(port, buf, 5) != PORT_ERR_OK)
+ return STM32_ERR_UNKNOWN;
+
+ if (buf[4] != (buf[0] ^ buf[1] ^ buf[2] ^ buf[3]))
+ return STM32_ERR_UNKNOWN;
+
+ *crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ return STM32_ERR_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_MSBMASK 0x80000000
+#define CRC_INIT_VALUE 0xFFFFFFFF
+uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len)
+{
+ int i;
+ 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 (i = 0; i < 32; i++)
+ if (crc & CRC_MSBMASK)
+ crc = (crc << 1) ^ CRCPOLY_BE;
+ else
+ crc = (crc << 1);
+ }
+ return crc;
+}
+
+stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address,
+ uint32_t length, uint32_t *crc)
+{
+ uint8_t buf[256];
+ uint32_t start, total_len, len, current_crc;
+
+ if (address & 0x3 || length & 0x3) {
+ fprintf(stderr, "Start and end addresses must be 4 byte aligned\n");
+ return STM32_ERR_UNKNOWN;
+ }
+
+ if (stm->cmd->crc != STM32_CMD_ERR)
+ return stm32_crc_memory(stm, address, length, crc);
+
+ start = address;
+ total_len = length;
+ current_crc = CRC_INIT_VALUE;
+ while (length) {
+ len = length > 256 ? 256 : length;
+ if (stm32_read_memory(stm, address, buf, len) != STM32_ERR_OK) {
+ fprintf(stderr,
+ "Failed to read memory at address 0x%08x, target write-protected?\n",
+ address);
+ return STM32_ERR_UNKNOWN;
+ }
+ current_crc = stm32_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 STM32_ERR_OK;
+}
diff --git a/stm32.h b/stm32.h
index fab3941..1688fcb 100644
--- a/stm32.h
+++ b/stm32.h
@@ -24,12 +24,23 @@
#include <stdint.h>
#include "serial.h"
+#define STM32_MAX_RX_FRAME 256 /* cmd read memory */
+#define STM32_MAX_TX_FRAME (1 + 256 + 1) /* cmd write memory */
+
+typedef enum {
+ STM32_ERR_OK = 0,
+ STM32_ERR_UNKNOWN, /* Generic error */
+ STM32_ERR_NACK,
+ STM32_ERR_NO_CMD, /* Command not available in bootloader */
+} stm32_err_t;
+
typedef struct stm32 stm32_t;
typedef struct stm32_cmd stm32_cmd_t;
typedef struct stm32_dev stm32_dev_t;
struct stm32 {
const serial_t *serial;
+ struct port_interface *port;
uint8_t bl_version;
uint8_t version;
uint8_t option1, option2;
@@ -49,16 +60,25 @@ struct stm32_dev {
uint32_t mem_start, mem_end;
};
-stm32_t* stm32_init (const serial_t *serial, const char init);
-void stm32_close (stm32_t *stm);
-char stm32_read_memory (const stm32_t *stm, uint32_t address, uint8_t data[], unsigned int len);
-char stm32_write_memory (const stm32_t *stm, uint32_t address, const uint8_t data[], unsigned int len);
-char stm32_wunprot_memory(const stm32_t *stm);
-char stm32_erase_memory (const stm32_t *stm, uint8_t spage, uint8_t pages);
-char stm32_go (const stm32_t *stm, uint32_t address);
-char stm32_reset_device (const stm32_t *stm);
-char stm32_readprot_memory (const stm32_t *stm);
-char stm32_runprot_memory (const stm32_t *stm);
+stm32_t *stm32_init(struct port_interface *port, const char init);
+void stm32_close(stm32_t *stm);
+stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address,
+ uint8_t data[], unsigned int len);
+stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address,
+ const uint8_t data[], unsigned int len);
+stm32_err_t stm32_wunprot_memory(const stm32_t *stm);
+stm32_err_t stm32_wprot_memory(const stm32_t *stm);
+stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage,
+ uint8_t pages);
+stm32_err_t stm32_go(const stm32_t *stm, uint32_t address);
+stm32_err_t stm32_reset_device(const stm32_t *stm);
+stm32_err_t stm32_readprot_memory(const stm32_t *stm);
+stm32_err_t stm32_runprot_memory(const stm32_t *stm);
+stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address,
+ uint32_t length, uint32_t *crc);
+stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address,
+ uint32_t length, uint32_t *crc);
+uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len);
#endif
diff --git a/stm32flash.1 b/stm32flash.1
index 44e9ed7..d37292f 100644
--- a/stm32flash.1
+++ b/stm32flash.1
@@ -1,9 +1,11 @@
.TH STM32FLASH 1 "2013\-11\-03" STM32FLASH "User command"
.SH NAME
-stm32flash \- UART flashing utility for STM32 and STM32W
+stm32flash \- flashing utility for STM32 and STM32W through UART or I2C
.SH SYNOPSIS
.B stm32flash
-.RB [ \-cfhjkouv ]
+.RB [ \-cfhjkouvCR ]
+.RB [ \-a
+.IR bus_address ]
.RB [ \-b
.IR baud_rate ]
.RB [ \-m
@@ -21,10 +23,14 @@ stm32flash \- UART flashing utility for STM32 and STM32W
.RB [ \-s
.IR start_page ]
.RB [ \-S
-.IR address [: length ]
+.IR address [: length ]]
+.RB [ \-F
+.IR RX_length [: TX_length ]]
.RB [ \-i
.IR GPIO_string ]
-.I tty_device
+.RI [ tty_device
+.R |
+.IR i2c_device ]
.SH DESCRIPTION
.B stm32flash
@@ -39,6 +45,12 @@ to interact with the bootloader of STM32[W].
.SH OPTIONS
.TP
+.BI "\-a" " bus_address"
+Specify address on bus for
+.IR i2c_device .
+This option is mandatory for I2C interface.
+
+.TP
.BI "\-b" " baud_rate"
Specify baud rate speed of
.IR tty_device .
@@ -104,7 +116,9 @@ Erase only.
.BI "\-e" " num"
Specify to erase only
.I num
-pages before writing the flash. Default is to erase the whole flash.
+pages before writing the flash. Default is to erase the whole flash. With
+.B \-e 0
+the flash would not be erased.
.TP
.B \-v
@@ -126,7 +140,15 @@ Specify flash page offset (0 = flash start).
.TP
.BI "\-S" " address" "[:" "length" "]"
-Specify start address and optionally length for read/write/erase operations.
+Specify start address and optionally length for read/write/erase/crc operations.
+
+.TP
+.BI "\-F" " RX_length" "[:" "TX_length" "]"
+Specify the maximum frame size for the current interface.
+Due to STM32 bootloader protocol, host will never handle frames bigger than
+256 byte in RX or 258 byte in TX.
+Due to current code, lowest limit in RX is 20 byte (to read a complete reply
+of command GET). Minimum limit in TX is 5 byte, required by protocol.
.TP
.B \-f
@@ -154,6 +176,25 @@ for the format of
.I GPIO_string
and further explanation).
+.TP
+.B \-C
+Specify to compute CRC on memory content.
+By default the CRC is computed on the whole flash content.
+Use
+.B "\-S"
+to provide different memory address range.
+
+.TP
+.B \-R
+Specify to reset the device at exit.
+This option is ignored if either
+.BR "\-g" ","
+.BR "\-j" ","
+.B "\-k"
+or
+.B "\-u"
+is also specified.
+
.SH BOOTLOADER GPIO SEQUENCE
This feature is currently available on Linux host only.
@@ -295,30 +336,65 @@ The original software package
.B stm32flash
is written by
.I Geoffrey McRae <geoff@spacevs.com>
-and available in http://stm32flash.googlecode.com .
-
-By November 2012 the project is migrated to
-https://gitorious.org/stm32flash
-and maintained by
+and is since 2012 maintained by
.IR "Tormod Volden <debian.tormod@gmail.com>" .
-Extension to STM32W and man page are written by
+Man page and extension to STM32W and I2C are written by
.IR "Antonio Borneo <borneo.antonio@gmail.com>" .
+Please report any bugs at the project homepage
+http://stm32flash.googlecode.com .
+
.SH SEE ALSO
.BR "srec_cat" "(1)," " srec_intel" "(5)," " srec_motorola" "(5)."
-The communication protocol used by ST bootloader is documented in ST
-application note AN3155. Copy is include in the source code of this
-software. Updated version of the application note is available in ST
-website:
+The communication protocol used by ST bootloader is documented in
+following ST application notes, depending on communication port.
+The current version of
+.B stm32flash
+only supports
+.I UART
+and
+.I I2C
+ports.
.PD 0
.P
+.IP \(bu 2
+AN3154: CAN protocol used in the STM32 bootloader
+.P
+.RS
+http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf
+.RE
+
+.P
+.IP \(bu 2
+AN3155: USART protocol used in the STM32(TM) bootloader
+.P
+.RS
http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf
+.RE
+
+.P
+.IP \(bu 2
+AN4221: I2C protocol used in the STM32 bootloader
+.P
+.RS
+http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf
+.RE
+
+.P
+.IP \(bu 2
+AN4286: SPI protocol used in the STM32 bootloader
+.P
+.RS
+http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf
+.RE
+
.PD
+
Boot mode selection for STM32 is documented in ST application note
-AN2606, available in ST website:
+AN2606, available from the ST website:
.PD 0
.P
http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf
diff --git a/utils.c b/utils.c
index 18947e0..271bb3e 100644
--- a/utils.c
+++ b/utils.c
@@ -17,7 +17,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
+#include <stdint.h>
#include "utils.h"
/* detect CPU endian */
@@ -42,4 +42,4 @@ uint32_t le_u32(const uint32_t v) {
((v & 0x0000FF00) << 8) |
((v & 0x000000FF) << 24);
return v;
-} \ No newline at end of file
+}